Skip to content

Commit 09f3d6f

Browse files
author
Nicolas Gallagher
committed
0.4.0
1 parent aed0c3e commit 09f3d6f

16 files changed

+621
-1
lines changed

dist/amd/currency.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
define(['react', './generator', 'globalize/currency'], function (React, generator) {
2+
3+
'use strict';
4+
5+
return generator("formatCurrency", ["value", "currency", "options"]);
6+
7+
});

dist/amd/date.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
define(['react', './generator', 'globalize/date'], function (React, generator) {
2+
3+
'use strict';
4+
5+
return generator("formatDate", ["value", "options"]);
6+
7+
});

dist/amd/generator.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
define(['./react-create-class', 'react', 'globalize'], function (reactCreateClass, React, Globalize) {
2+
3+
'use strict';
4+
5+
var commonPropNames = ["elements", "locale"];
6+
7+
function capitalizeFirstLetter(string) {
8+
return string.charAt(0).toUpperCase() + string.slice(1);
9+
}
10+
11+
function omit(set) {
12+
return function(element) {
13+
return set.indexOf(element) === -1;
14+
};
15+
}
16+
17+
function generator(fn, localPropNames, options) {
18+
options = options || {};
19+
var Fn = capitalizeFirstLetter(fn);
20+
var beforeFormat = options.beforeFormat || function() {};
21+
var afterFormat = options.afterFormat || function(formattedValue) {
22+
return formattedValue;
23+
};
24+
var globalizePropNames = commonPropNames.concat(localPropNames);
25+
26+
return reactCreateClass({
27+
displayName: Fn,
28+
componentWillMount: function() {
29+
this.setup(this.props);
30+
},
31+
componentWillReceiveProps: function(nextProps) {
32+
this.setup(nextProps);
33+
},
34+
setup: function(props) {
35+
this.globalize = props.locale ? Globalize(props.locale) : Globalize;
36+
this.domProps = Object.keys(props).filter(omit(globalizePropNames)).reduce(function(memo, propKey) {
37+
memo[propKey] = props[propKey];
38+
return memo;
39+
}, {});
40+
41+
this.globalizePropValues = localPropNames.map(function(element) {
42+
return props[element];
43+
});
44+
this.globalizePropValues[0] = props.children;
45+
46+
beforeFormat.call(this, props);
47+
var formattedValue = this.globalize[fn].apply(this.globalize, this.globalizePropValues);
48+
this.value = afterFormat.call(this, formattedValue);
49+
},
50+
render: function() {
51+
return React.DOM.span(this.domProps, this.value);
52+
}
53+
});
54+
}
55+
56+
57+
return generator;
58+
59+
});

dist/amd/message.js

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
define(['globalize', 'react', './generator', 'globalize/message', 'globalize/plural'], function (Globalize, React, generator) {
2+
3+
'use strict';
4+
5+
function messageSetup(globalize, props, globalizePropValues) {
6+
var defaultMessage;
7+
var children = props.children;
8+
var scope = props.scope;
9+
10+
function getDefaultMessage(children) {
11+
if (typeof children === "string") {
12+
return children;
13+
} else {
14+
throw new Error("Invalid default message type `" + typeof children + "`");
15+
}
16+
}
17+
18+
// Set path - path as props supercedes default value.
19+
if (props.path) {
20+
// Override generator assumption. The generator assumes the globalizePropValues[0]
21+
// (path) and props.children to be mutually exclusive, but this isn't
22+
// true here for messages. Because, it's possible to use props.path (for
23+
// path) and props.children for defaultMessage, which are two different
24+
// variables.
25+
globalizePropValues[0] = props.path;
26+
} else {
27+
// Although the generator had already set globalizePropValues[0] (path) as
28+
// props.children, here its type is checked and its value is sanitized.
29+
defaultMessage = getDefaultMessage(children);
30+
globalizePropValues[0] = sanitizePath(defaultMessage);
31+
}
32+
33+
// Scope path.
34+
if (scope) {
35+
globalizePropValues[0] = scope + "/" + globalizePropValues[0];
36+
}
37+
38+
// Development mode only.
39+
if (process.env.NODE_ENV !== "production") {
40+
var path = props.path ? props.path.split("/") : [globalizePropValues[0]];
41+
var getMessage = function(globalize, path) {
42+
return globalize.cldr.get(["globalize-messages/{bundle}"].concat(path));
43+
};
44+
45+
var setMessage = function(globalize, path, message) {
46+
var data = {};
47+
function set(data, path, value) {
48+
var i;
49+
var node = data;
50+
var length = path.length;
51+
52+
for (i = 0; i < length - 1; i++) {
53+
if (!node[path[i]]) {
54+
node[path[i]] = {};
55+
}
56+
node = node[path[i]];
57+
}
58+
node[path[i]] = value;
59+
}
60+
set(data, [globalize.cldr.attributes.bundle].concat(path), message);
61+
Globalize.loadMessages(data);
62+
};
63+
64+
if (globalize.cldr) {
65+
if (!getMessage(globalize, path)) {
66+
defaultMessage = defaultMessage || getDefaultMessage(children);
67+
setMessage(globalize, path, defaultMessage);
68+
}
69+
}
70+
}
71+
}
72+
73+
function replaceElements(props, formatted) {
74+
var elements = props.elements;
75+
76+
function _replaceElements(string, elements) {
77+
if (typeof string !== "string") {
78+
throw new Error("Missing or invalid string `" + string + "` (" + typeof string + ")");
79+
}
80+
if (typeof elements !== "object") {
81+
throw new Error("Missing or invalid elements `" + elements + "` (" + typeof elements + ")");
82+
}
83+
84+
// Given [x, y, z], it returns [x, element, y, element, z].
85+
function spreadElementsInBetweenItems(array, element) {
86+
var getElement = typeof element === "function" ? element : function() {
87+
return element;
88+
};
89+
return array.slice(1).reduce(function(ret, item, i) {
90+
ret.push(getElement(i), item);
91+
return ret;
92+
}, [array[0]]);
93+
}
94+
95+
function splice(sourceArray, start, deleteCount, itemsArray) {
96+
[].splice.apply(sourceArray, [start, deleteCount].concat(itemsArray));
97+
}
98+
99+
return Object.keys(elements).reduce(function(ret, key) {
100+
var element = elements[key];
101+
102+
ret.forEach(function(string, i) {
103+
var aux, contents, regexp, regexp2;
104+
105+
// Insert array into the correct ret position.
106+
function replaceRetItem(array) {
107+
splice(ret, i, 1, array);
108+
}
109+
110+
if (typeof string !== "string") {
111+
return; // continue;
112+
}
113+
114+
// Empty tags, e.g., `[foo/]`.
115+
aux = string.split("[" + key + "/]");
116+
if (aux.length > 1) {
117+
aux = spreadElementsInBetweenItems(aux, element);
118+
replaceRetItem(aux);
119+
return; // continue;
120+
}
121+
122+
// Start-end tags, e.g., `[foo]content[/foo]`.
123+
regexp = new RegExp("\\[" + key + "\\][\\s\\S]*?\\[\\/" + key + "\\]", "g");
124+
regexp2 = new RegExp("\\[" + key + "\\]([\\s\\S]*?)\\[\\/" + key + "\\]");
125+
aux = string.split(regexp);
126+
if (aux.length > 1) {
127+
contents = string.match(regexp).map(function(content) {
128+
return content.replace(regexp2, "$1");
129+
});
130+
aux = spreadElementsInBetweenItems(aux, function(i) {
131+
return React.cloneElement(element, {}, contents[i]);
132+
});
133+
replaceRetItem(aux);
134+
}
135+
});
136+
137+
return ret;
138+
}, [string]);
139+
}
140+
141+
142+
// Elements replacement.
143+
if (elements) {
144+
formatted = React.DOM.span.apply(React.DOM.span, [{}].concat(_replaceElements(formatted, elements)));
145+
}
146+
147+
return formatted;
148+
}
149+
150+
function sanitizePath(pathString) {
151+
return pathString.trim().replace(/\{/g, "(").replace(/\}/g, ")").replace(/\//g, "|").replace(/\n/g, " ").replace(/ +/g, " ").replace(/"/g, "'");
152+
}
153+
154+
// Overload Globalize's `.formatMessage` to allow default message.
155+
var globalizeMessageFormatter = Globalize.messageFormatter;
156+
Globalize.messageFormatter = Globalize.prototype.messageFormatter = function(pathOrMessage) {
157+
var aux = {};
158+
var sanitizedPath = sanitizePath(pathOrMessage);
159+
160+
// Globalize runtime
161+
if (!this.cldr) {
162+
// On runtime, the only way for deciding between using sanitizedPath or
163+
// pathOrMessage as path is by checking which formatter exists.
164+
arguments[0] = sanitizedPath;
165+
aux = globalizeMessageFormatter.apply(this, arguments);
166+
arguments[0] = pathOrMessage;
167+
return aux || globalizeMessageFormatter.apply(this, arguments);
168+
}
169+
170+
var sanitizedPathExists = this.cldr.get(["globalize-messages/{bundle}", sanitizedPath]) !== undefined;
171+
var pathExists = this.cldr.get(["globalize-messages/{bundle}", pathOrMessage]) !== undefined;
172+
173+
// Want to distinguish between default message and path value - just checking
174+
// for sanitizedPath won't be enough, because sanitizedPath !== pathOrMessage
175+
// for paths like "salutations/hi".
176+
if (!sanitizedPathExists && !pathExists) {
177+
aux[this.cldr.attributes.bundle] = {};
178+
aux[this.cldr.attributes.bundle][sanitizedPath] = pathOrMessage;
179+
Globalize.loadMessages(aux);
180+
sanitizedPathExists = true;
181+
}
182+
183+
arguments[0] = sanitizedPathExists ? sanitizedPath : pathOrMessage;
184+
return globalizeMessageFormatter.apply(this, arguments);
185+
};
186+
187+
188+
return generator("formatMessage", ["path", "variables"], {
189+
beforeFormat: function(props) {
190+
messageSetup(this.globalize, props, this.globalizePropValues);
191+
},
192+
afterFormat: function(formattedValue) {
193+
return replaceElements(this.props, formattedValue);
194+
}
195+
});
196+
197+
});

dist/amd/number.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
define(['react', './generator', 'globalize/number'], function (React, generator) {
2+
3+
'use strict';
4+
5+
return generator("formatNumber", ["value", "options"]);
6+
7+
});

dist/amd/react-create-class.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
define(['react'], function (React) {
2+
3+
'use strict';
4+
5+
var [major, minor] = React.version.split(".");
6+
7+
var REACT15 = major === "15";
8+
var REACT155 = REACT15 && minor >= 5;
9+
10+
var reactCreateClass;
11+
if (REACT155) {
12+
reactCreateClass = require("create-react-class");
13+
} else {
14+
reactCreateClass = React.createClass;
15+
}
16+
17+
18+
return reactCreateClass;
19+
20+
});

dist/amd/relative-time.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
define(['react', './generator', 'globalize/relative-time'], function (React, generator) {
2+
3+
'use strict';
4+
5+
return generator("formatRelativeTime", ["value", "unit", "options"]);
6+
7+
});

dist/currency.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
var React = require('react');
4+
var generator = require('./generator');
5+
6+
7+
module.exports = generator("formatCurrency", ["value", "currency", "options"]);

dist/date.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
var React = require('react');
4+
var generator = require('./generator');
5+
6+
7+
module.exports = generator("formatDate", ["value", "options"]);

dist/generator.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
var reactCreateClass = require('./react-create-class');
4+
var React = require('react');
5+
var Globalize = require('globalize');
6+
7+
var commonPropNames = ["elements", "locale"];
8+
9+
function capitalizeFirstLetter(string) {
10+
return string.charAt(0).toUpperCase() + string.slice(1);
11+
}
12+
13+
function omit(set) {
14+
return function(element) {
15+
return set.indexOf(element) === -1;
16+
};
17+
}
18+
19+
function generator(fn, localPropNames, options) {
20+
options = options || {};
21+
var Fn = capitalizeFirstLetter(fn);
22+
var beforeFormat = options.beforeFormat || function() {};
23+
var afterFormat = options.afterFormat || function(formattedValue) {
24+
return formattedValue;
25+
};
26+
var globalizePropNames = commonPropNames.concat(localPropNames);
27+
28+
return reactCreateClass({
29+
displayName: Fn,
30+
componentWillMount: function() {
31+
this.setup(this.props);
32+
},
33+
componentWillReceiveProps: function(nextProps) {
34+
this.setup(nextProps);
35+
},
36+
setup: function(props) {
37+
this.globalize = props.locale ? Globalize(props.locale) : Globalize;
38+
this.domProps = Object.keys(props).filter(omit(globalizePropNames)).reduce(function(memo, propKey) {
39+
memo[propKey] = props[propKey];
40+
return memo;
41+
}, {});
42+
43+
this.globalizePropValues = localPropNames.map(function(element) {
44+
return props[element];
45+
});
46+
this.globalizePropValues[0] = props.children;
47+
48+
beforeFormat.call(this, props);
49+
var formattedValue = this.globalize[fn].apply(this.globalize, this.globalizePropValues);
50+
this.value = afterFormat.call(this, formattedValue);
51+
},
52+
render: function() {
53+
return React.DOM.span(this.domProps, this.value);
54+
}
55+
});
56+
}
57+
58+
module.exports = generator;

0 commit comments

Comments
 (0)