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
+ /* eslint-disable no-inner-declarations */
42
+ function getMessage ( globalize , path ) {
43
+ return globalize . cldr . get ( [ "globalize-messages/{bundle}" ] . concat ( path ) ) ;
44
+ }
45
+
46
+ function setMessage ( globalize , path , message ) {
47
+ var data = { } ;
48
+ function set ( data , path , value ) {
49
+ var i ;
50
+ var node = data ;
51
+ var length = path . length ;
52
+
53
+ for ( i = 0 ; i < length - 1 ; i ++ ) {
54
+ if ( ! node [ path [ i ] ] ) {
55
+ node [ path [ i ] ] = { } ;
56
+ }
57
+ node = node [ path [ i ] ] ;
58
+ }
59
+ node [ path [ i ] ] = value ;
60
+ }
61
+ set ( data , [ globalize . cldr . attributes . bundle ] . concat ( path ) , message ) ;
62
+ Globalize . loadMessages ( data ) ;
63
+ }
64
+ /* eslint-enable no-inner-declarations */
65
+
66
+ if ( globalize . cldr ) {
67
+ if ( ! getMessage ( globalize , path ) ) {
68
+ defaultMessage = defaultMessage || getDefaultMessage ( children ) ;
69
+ setMessage ( globalize , path , defaultMessage ) ;
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ function replaceElements ( props , formatted ) {
76
+ var elements = props . elements ;
77
+
78
+ function _replaceElements ( string , elements ) {
79
+ if ( typeof string !== "string" ) {
80
+ throw new Error ( "Missing or invalid string `" + string + "` (" + typeof string + ")" ) ;
81
+ }
82
+ if ( typeof elements !== "object" ) {
83
+ throw new Error ( "Missing or invalid elements `" + elements + "` (" + typeof elements + ")" ) ;
84
+ }
85
+
86
+ // Given [x, y, z], it returns [x, element, y, element, z].
87
+ function spreadElementsInBetweenItems ( array , element ) {
88
+ var getElement = typeof element === "function" ? element : function ( ) {
89
+ return element ;
90
+ } ;
91
+ return array . slice ( 1 ) . reduce ( function ( ret , item , i ) {
92
+ ret . push ( getElement ( i ) , item ) ;
93
+ return ret ;
94
+ } , [ array [ 0 ] ] ) ;
95
+ }
96
+
97
+ function splice ( sourceArray , start , deleteCount , itemsArray ) {
98
+ [ ] . splice . apply ( sourceArray , [ start , deleteCount ] . concat ( itemsArray ) ) ;
99
+ }
100
+
101
+ return Object . keys ( elements ) . reduce ( function ( ret , key ) {
102
+ var element = elements [ key ] ;
103
+
104
+ ret . forEach ( function ( string , i ) {
105
+ var aux , contents , regexp , regexp2 ;
106
+
107
+ // Insert array into the correct ret position.
108
+ function replaceRetItem ( array ) {
109
+ splice ( ret , i , 1 , array ) ;
110
+ }
111
+
112
+ if ( typeof string !== "string" ) {
113
+ return ; // continue;
114
+ }
115
+
116
+ // Empty tags, e.g., `[foo/]`.
117
+ aux = string . split ( "[" + key + "/]" ) ;
118
+ if ( aux . length > 1 ) {
119
+ aux = spreadElementsInBetweenItems ( aux , element ) ;
120
+ replaceRetItem ( aux ) ;
121
+ return ; // continue;
122
+ }
123
+
124
+ // Start-end tags, e.g., `[foo]content[/foo]`.
125
+ regexp = new RegExp ( "\\[" + key + "\\][\\s\\S]*?\\[\\/" + key + "\\]" , "g" ) ;
126
+ regexp2 = new RegExp ( "\\[" + key + "\\]([\\s\\S]*?)\\[\\/" + key + "\\]" ) ;
127
+ aux = string . split ( regexp ) ;
128
+ if ( aux . length > 1 ) {
129
+ contents = string . match ( regexp ) . map ( function ( content ) {
130
+ return content . replace ( regexp2 , "$1" ) ;
131
+ } ) ;
132
+ aux = spreadElementsInBetweenItems ( aux , function ( i ) {
133
+ return React . cloneElement ( element , { } , contents [ i ] ) ;
134
+ } ) ;
135
+ replaceRetItem ( aux ) ;
136
+ }
137
+ } ) ;
138
+
139
+ return ret ;
140
+ } , [ string ] ) ;
141
+ }
142
+
143
+
144
+ // Elements replacement.
145
+ if ( elements ) {
146
+ formatted = React . DOM . span . apply ( React . DOM . span , [ { } ] . concat ( _replaceElements ( formatted , elements ) ) ) ;
147
+ }
148
+
149
+ return formatted ;
150
+ }
151
+
152
+ function sanitizePath ( pathString ) {
153
+ return pathString . trim ( ) . replace ( / \{ / g, "(" ) . replace ( / \} / g, ")" ) . replace ( / \/ / g, "|" ) . replace ( / \n / g, " " ) . replace ( / + / g, " " ) . replace ( / " / g, "'" ) ;
154
+ }
155
+
156
+ // Overload Globalize's `.formatMessage` to allow default message.
157
+ var globalizeMessageFormatter = Globalize . messageFormatter ;
158
+ Globalize . messageFormatter = Globalize . prototype . messageFormatter = function ( pathOrMessage ) {
159
+ var aux = { } ;
160
+ var sanitizedPath = sanitizePath ( pathOrMessage ) ;
161
+
162
+ // Globalize runtime
163
+ if ( ! this . cldr ) {
164
+ // On runtime, the only way for deciding between using sanitizedPath or
165
+ // pathOrMessage as path is by checking which formatter exists.
166
+ arguments [ 0 ] = sanitizedPath ;
167
+ aux = globalizeMessageFormatter . apply ( this , arguments ) ;
168
+ arguments [ 0 ] = pathOrMessage ;
169
+ return aux || globalizeMessageFormatter . apply ( this , arguments ) ;
170
+ }
171
+
172
+ var sanitizedPathExists = this . cldr . get ( [ "globalize-messages/{bundle}" , sanitizedPath ] ) !== undefined ;
173
+ var pathExists = this . cldr . get ( [ "globalize-messages/{bundle}" , pathOrMessage ] ) !== undefined ;
174
+
175
+ // Want to distinguish between default message and path value - just checking
176
+ // for sanitizedPath won't be enough, because sanitizedPath !== pathOrMessage
177
+ // for paths like "salutations/hi".
178
+ if ( ! sanitizedPathExists && ! pathExists ) {
179
+ aux [ this . cldr . attributes . bundle ] = { } ;
180
+ aux [ this . cldr . attributes . bundle ] [ sanitizedPath ] = pathOrMessage ;
181
+ Globalize . loadMessages ( aux ) ;
182
+ sanitizedPathExists = true ;
183
+ }
184
+
185
+ arguments [ 0 ] = sanitizedPathExists ? sanitizedPath : pathOrMessage ;
186
+ return globalizeMessageFormatter . apply ( this , arguments ) ;
187
+ } ;
188
+
189
+
190
+ return generator ( "formatMessage" , [ "path" , "variables" ] , {
191
+ beforeFormat : function ( ) {
192
+ messageSetup ( this . globalize , this . props , this . globalizePropValues ) ;
193
+ } ,
194
+ afterFormat : function ( formattedValue ) {
195
+ return replaceElements ( this . props , formattedValue ) ;
196
+ }
197
+ } ) ;
198
+
199
+ } ) ;
0 commit comments