hanzi-rails 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{vendor/assets/stylesheets/font → app/assets/fonts}/han.otf +0 -0
- data/{vendor/assets/stylesheets/font → app/assets/fonts}/han.ttf +0 -0
- data/{vendor/assets/stylesheets/font → app/assets/fonts}/han.woff +0 -0
- data/app/assets/javascripts/hanzi.js +2375 -0
- data/app/assets/stylesheets/hanzi.css.erb +2283 -0
- data/lib/hanzi-rails/engine.rb +6 -0
- data/lib/{hanzi/rails → hanzi-rails}/version.rb +1 -1
- data/lib/hanzi-rails.rb +2 -8
- metadata +18 -11
- data/vendor/assets/javascripts/hanzi.js +0 -4
- data/vendor/assets/stylesheets/hanzi.css +0 -6
@@ -0,0 +1,2375 @@
|
|
1
|
+
/*!
|
2
|
+
* 漢字標準格式 v3.1.0 | MIT License | css.hanzi.co
|
3
|
+
* Han.css: the CSS typography framework optimised for Hanzi
|
4
|
+
*/
|
5
|
+
|
6
|
+
void function( global, factory ) {
|
7
|
+
|
8
|
+
// CommonJS
|
9
|
+
if ( typeof module === 'object' && typeof module.exports === 'object' ) {
|
10
|
+
module.exports = factory( global, true )
|
11
|
+
} else {
|
12
|
+
factory( global )
|
13
|
+
}
|
14
|
+
|
15
|
+
}( typeof window !== 'undefined' ? window : this, function( window, noGlobalNS ) {
|
16
|
+
|
17
|
+
'use strict'
|
18
|
+
|
19
|
+
var document = window.document
|
20
|
+
|
21
|
+
var root = document.documentElement
|
22
|
+
|
23
|
+
var body = document.body
|
24
|
+
|
25
|
+
var VERSION = '3.1.0'
|
26
|
+
|
27
|
+
var ROUTINE = [
|
28
|
+
// Initialise the condition with feature-detecting
|
29
|
+
// classes (Modernizr-alike), binding onto the root
|
30
|
+
// element, possibly `<html>`.
|
31
|
+
'initCond',
|
32
|
+
// Address element normalisation
|
33
|
+
'renderElem',
|
34
|
+
// Handle Biaodian
|
35
|
+
//'jinzify',
|
36
|
+
'renderJiya',
|
37
|
+
// Address Hanzi and Western script mixed spacing
|
38
|
+
'renderHWS',
|
39
|
+
// Address Basic Biaodian correction in Firefox
|
40
|
+
'correctBasicBD',
|
41
|
+
// Address presentational correction to combining ligatures
|
42
|
+
'substCombLigaWithPUA'
|
43
|
+
// Address semantic correction to inaccurate characters
|
44
|
+
// **Note:** inactivated by default
|
45
|
+
// 'substInaccurateChar'
|
46
|
+
]
|
47
|
+
|
48
|
+
// Define Han
|
49
|
+
var Han = function( context, condition ) {
|
50
|
+
return new Han.fn.init( context, condition )
|
51
|
+
}
|
52
|
+
|
53
|
+
var init = function() {
|
54
|
+
if ( arguments[ 0 ] ) {
|
55
|
+
this.context = arguments[ 0 ]
|
56
|
+
}
|
57
|
+
if ( arguments[ 1 ] ) {
|
58
|
+
this.condition = arguments[ 1 ]
|
59
|
+
}
|
60
|
+
return this
|
61
|
+
}
|
62
|
+
|
63
|
+
Han.version = VERSION
|
64
|
+
|
65
|
+
Han.fn = Han.prototype = {
|
66
|
+
version: VERSION,
|
67
|
+
|
68
|
+
constructor: Han,
|
69
|
+
|
70
|
+
// Body as the default target context
|
71
|
+
context: body,
|
72
|
+
|
73
|
+
// Root element as the default condition
|
74
|
+
condition: root,
|
75
|
+
|
76
|
+
// Default rendering routine
|
77
|
+
routine: ROUTINE,
|
78
|
+
|
79
|
+
init: init,
|
80
|
+
|
81
|
+
setRoutine: function( routine ) {
|
82
|
+
if ( Array.isArray( routine )) {
|
83
|
+
this.routine = routine
|
84
|
+
}
|
85
|
+
return this
|
86
|
+
},
|
87
|
+
|
88
|
+
// Note that the routine set up here will execute
|
89
|
+
// only once. The method won't alter the routine in
|
90
|
+
// the instance or in the prototype chain.
|
91
|
+
render: function( routine ) {
|
92
|
+
var it = this
|
93
|
+
var routine = Array.isArray( routine ) ? routine : this.routine
|
94
|
+
|
95
|
+
routine
|
96
|
+
.forEach(function( method ) {
|
97
|
+
try {
|
98
|
+
if ( typeof method === 'string' ) {
|
99
|
+
it[ method ]()
|
100
|
+
} else if ( Array.isArray( method )) {
|
101
|
+
it[ method.shift() ].apply( it, method )
|
102
|
+
}
|
103
|
+
} catch ( e ) {}
|
104
|
+
})
|
105
|
+
return this
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
Han.fn.init.prototype = Han.fn
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Shortcut for `render()` under the default
|
113
|
+
* situation.
|
114
|
+
*
|
115
|
+
* Once initialised, replace `Han.init` with the
|
116
|
+
* instance for future usage.
|
117
|
+
*/
|
118
|
+
Han.init = function() {
|
119
|
+
return Han.init = Han().render()
|
120
|
+
}
|
121
|
+
|
122
|
+
var UNICODE = {
|
123
|
+
/**
|
124
|
+
* Western punctuation (西文標點符號)
|
125
|
+
*/
|
126
|
+
punct: {
|
127
|
+
base: '[\u2026,.;:!?\u203D_]',
|
128
|
+
sing: '[\u2010-\u2014\u2026]',
|
129
|
+
middle: '[\\\/~\\-&\u2010-\u2014_]',
|
130
|
+
open: '[\'"‘“\\(\\[\u00A1\u00BF\u2E18\u00AB\u2039\u201A\u201C\u201E]',
|
131
|
+
close: '[\'"”’\\)\\]\u00BB\u203A\u201B\u201D\u201F]',
|
132
|
+
end: '[\'"”’\\)\\]\u00BB\u203A\u201B\u201D\u201F\u203C\u203D\u2047-\u2049,.;:!?]',
|
133
|
+
},
|
134
|
+
|
135
|
+
/**
|
136
|
+
* CJK biaodian (CJK標點符號)
|
137
|
+
*/
|
138
|
+
biaodian: {
|
139
|
+
base: '[︰.、,。:;?!ー]',
|
140
|
+
liga: '[—…⋯]',
|
141
|
+
middle: '[·\/-゠\uFF06\u30FB\uFF3F]',
|
142
|
+
open: '[「『《〈(〔[{【〖]',
|
143
|
+
close: '[」』》〉)〕]}】〗]',
|
144
|
+
end: '[」』》〉)〕]}】〗︰.、,。:;?!ー]'
|
145
|
+
},
|
146
|
+
|
147
|
+
/**
|
148
|
+
* CJK-related blocks (CJK相關字符區段)
|
149
|
+
*
|
150
|
+
* 1. 中日韓統一表意文字:[\u4E00-\u9FFF]
|
151
|
+
Basic CJK unified ideographs
|
152
|
+
* 2. 擴展-A區:[\u3400-\u4DB5]
|
153
|
+
Extended-A
|
154
|
+
* 3. 擴展-B區:[\u20000-\u2A6D6]([\uD840-\uD869][\uDC00-\uDED6])
|
155
|
+
Extended-B
|
156
|
+
* 4. 擴展-C區:[\u2A700-\u2B734](\uD86D[\uDC00-\uDF3F]|[\uD86A-\uD86C][\uDC00-\uDFFF]|\uD869[\uDF00-\uDFFF])
|
157
|
+
Extended-C
|
158
|
+
* 5. 擴展-D區:[\u2B740-\u2B81D](急用漢字,\uD86D[\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1F])
|
159
|
+
Extended-D
|
160
|
+
* 6. 擴展-E區:[\u2B820-\u2F7FF](暫未支援)
|
161
|
+
Extended-E (not supported yet)
|
162
|
+
* 7. 擴展-F區(暫未支援)
|
163
|
+
Extended-F (not supported yet)
|
164
|
+
* 8. 筆畫區:[\u31C0-\u31E3]
|
165
|
+
Strokes
|
166
|
+
* 9. 表意數字「〇」:[\u3007]
|
167
|
+
Ideographic number zero
|
168
|
+
* 10. 相容表意文字及補充:[\uF900-\uFAFF][\u2F800-\u2FA1D](不使用)
|
169
|
+
Compatibility ideograph and supplement (not supported)
|
170
|
+
|
171
|
+
12 exceptions:
|
172
|
+
[\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]
|
173
|
+
|
174
|
+
https://zh.wikipedia.org/wiki/中日韓統一表意文字#cite_note-1
|
175
|
+
|
176
|
+
* 11. 康熙字典及簡化字部首:[\u2F00-\u2FD5\u2E80-\u2EF3]
|
177
|
+
Kangxi and supplement radicals
|
178
|
+
* 12. 表意文字描述字元:[\u2FF0-\u2FFA]
|
179
|
+
Ideographic description characters
|
180
|
+
*/
|
181
|
+
hanzi: {
|
182
|
+
base: '[\u4E00-\u9FFF\u3400-\u4DB5\u31C0-\u31E3\u3007\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD800-\uDBFF][\uDC00-\uDFFF]',
|
183
|
+
desc: '[\u2FF0-\u2FFA]',
|
184
|
+
radical: '[\u2F00-\u2FD5\u2E80-\u2EF3]'
|
185
|
+
},
|
186
|
+
|
187
|
+
/**
|
188
|
+
* Latin script blocks (拉丁字母區段)
|
189
|
+
*
|
190
|
+
* 1. 基本拉丁字母:A-Za-z
|
191
|
+
Basic Latin
|
192
|
+
* 2. 阿拉伯數字:0-9
|
193
|
+
Digits
|
194
|
+
* 3. 補充-1:[\u00C0-\u00FF]
|
195
|
+
Latin-1 supplement
|
196
|
+
* 4. 擴展-A區:[\u0100-\u017F]
|
197
|
+
Extended-A
|
198
|
+
* 5. 擴展-B區:[\u0180-\u024F]
|
199
|
+
Extended-B
|
200
|
+
* 5. 擴展-C區:[\u2C60-\u2C7F]
|
201
|
+
Extended-C
|
202
|
+
* 5. 擴展-D區:[\uA720-\uA7FF]
|
203
|
+
Extended-D
|
204
|
+
* 6. 附加區:[\u1E00-\u1EFF]
|
205
|
+
Extended additional
|
206
|
+
* 7. 變音組字符:[\u0300-\u0341\u1DC0-\u1DFF]
|
207
|
+
Combining diacritical marks
|
208
|
+
*/
|
209
|
+
latin: {
|
210
|
+
base: '[A-Za-z0-9\u00C0-\u00FF\u0100-\u017F\u0180-\u024F\u2C60-\u2C7F\uA720-\uA7FF\u1E00-\u1EFF]',
|
211
|
+
combine: '[\u0300-\u0341\u1DC0-\u1DFF]'
|
212
|
+
},
|
213
|
+
|
214
|
+
/**
|
215
|
+
* Elli̱niká (Greek) script blocks (希臘字母區段)
|
216
|
+
*
|
217
|
+
* 1. 希臘字母及擴展:[\u0370–\u03FF\u1F00-\u1FFF]
|
218
|
+
Basic Greek & Greek Extended
|
219
|
+
* 2. 阿拉伯數字:0-9
|
220
|
+
Digits
|
221
|
+
* 3. 希臘字母變音組字符:[\u0300-\u0345\u1DC0-\u1DFF]
|
222
|
+
Combining diacritical marks
|
223
|
+
*/
|
224
|
+
ellinika: {
|
225
|
+
base: '[0-9\u0370-\u03FF\u1F00-\u1FFF]',
|
226
|
+
combine: '[\u0300-\u0345\u1DC0-\u1DFF]'
|
227
|
+
},
|
228
|
+
|
229
|
+
/**
|
230
|
+
* Kirillica (Cyrillic) script blocks (西里爾字母區段)
|
231
|
+
*
|
232
|
+
* 1. 西里爾字母及補充:[\u0400-\u0482\u048A-\u04FF\u0500-\u052F]
|
233
|
+
Basic Cyrillic and supplement
|
234
|
+
* 2. 擴展B區:[\uA640-\uA66E\uA67E-\uA697]
|
235
|
+
Extended-B
|
236
|
+
* 3. 阿拉伯數字:0-9
|
237
|
+
Digits
|
238
|
+
* 4. 西里爾字母組字符:[\u0483-\u0489\u2DE0-\u2DFF\uA66F-\uA67D\uA69F](位擴展A、B區)
|
239
|
+
Cyrillic combining diacritical marks (in extended-A, B)
|
240
|
+
*/
|
241
|
+
kirillica: {
|
242
|
+
base: '[0-9\u0400-\u0482\u048A-\u04FF\u0500-\u052F\uA640-\uA66E\uA67E-\uA697]',
|
243
|
+
combine: '[\u0483-\u0489\u2DE0-\u2DFF\uA66F-\uA67D\uA69F]'
|
244
|
+
},
|
245
|
+
|
246
|
+
/**
|
247
|
+
* Kana (假名)
|
248
|
+
*
|
249
|
+
* 1. 日文假名:[\u30A2\u30A4\u30A6\u30A8\u30AA-\u30FA\u3042\u3044\u3046\u3048\u304A-\u3094\u309F\u30FF]
|
250
|
+
Japanese Kana
|
251
|
+
* 2. 假名補充[\u1B000\u1B001](\uD82C[\uDC00-\uDC01])
|
252
|
+
Kana supplement
|
253
|
+
* 3. 日文假名小寫:[\u3041\u3043\u3045\u3047\u3049\u30A1\u30A3\u30A5\u30A7\u30A9\u3063\u3083\u3085\u3087\u308E\u3095\u3096\u30C3\u30E3\u30E5\u30E7\u30EE\u30F5\u30F6\u31F0-\u31FF]
|
254
|
+
Japanese small Kana
|
255
|
+
* 4. 假名組字符:[\u3099-\u309C]
|
256
|
+
Kana combining characters
|
257
|
+
* 5. 半形假名:[\uFF66-\uFF9F]
|
258
|
+
Halfwidth Kana
|
259
|
+
* 6. 符號:[\u309D\u309E\u30FB-\u30FE]
|
260
|
+
Marks
|
261
|
+
*/
|
262
|
+
kana: {
|
263
|
+
base: '[\u30A2\u30A4\u30A6\u30A8\u30AA-\u30FA\u3042\u3044\u3046\u3048\u304A-\u3094\u309F\u30FF]|\uD82C[\uDC00-\uDC01]',
|
264
|
+
small: '[\u3041\u3043\u3045\u3047\u3049\u30A1\u30A3\u30A5\u30A7\u30A9\u3063\u3083\u3085\u3087\u308E\u3095\u3096\u30C3\u30E3\u30E5\u30E7\u30EE\u30F5\u30F6\u31F0-\u31FF]',
|
265
|
+
combine: '[\u3099-\u309C]',
|
266
|
+
half: '[\uFF66-\uFF9F]',
|
267
|
+
mark: '[\u30A0\u309D\u309E\u30FB-\u30FE]'
|
268
|
+
},
|
269
|
+
|
270
|
+
/**
|
271
|
+
* Eonmun (Hangul, 諺文)
|
272
|
+
*
|
273
|
+
* 1. 諺文音節:[\uAC00-\uD7A3]
|
274
|
+
Eonmun (Hangul) syllables
|
275
|
+
* 2. 諺文字母:[\u1100-\u11FF\u314F-\u3163\u3131-\u318E\uA960-\uA97C\uD7B0-\uD7FB]
|
276
|
+
Eonmun (Hangul) letters
|
277
|
+
* 3. 半形諺文字母:[\uFFA1-\uFFDC]
|
278
|
+
Halfwidth Eonmun (Hangul) letters
|
279
|
+
*/
|
280
|
+
eonmun: {
|
281
|
+
base: '[\uAC00-\uD7A3]',
|
282
|
+
letter: '[\u1100-\u11FF\u314F-\u3163\u3131-\u318E\uA960-\uA97C\uD7B0-\uD7FB]',
|
283
|
+
half: '[\uFFA1-\uFFDC]'
|
284
|
+
},
|
285
|
+
|
286
|
+
/**
|
287
|
+
* Zhuyin (注音符號, Mandarin & Dialect Phonetic Symbols)
|
288
|
+
*
|
289
|
+
* 1. 國語注音、方言音符號:[\u3105-\u312D][\u31A0-\u31BA]
|
290
|
+
Bopomofo phonetic symbols
|
291
|
+
* 2. 國語陰陽上去聲調號:[\u02D9\u02CA\u02C5\u02C7\u02CB] (**註:**三聲包含乙個不合規範的符號)
|
292
|
+
Tones for Mandarin
|
293
|
+
* 3. 方言音陰、陽去聲調號:[\u02EA\u02EB]
|
294
|
+
Departing tones in dialects
|
295
|
+
* 4. 方言音陰、陽入韻:[\u31B4-\u31B7][\u0358\u030d]?
|
296
|
+
Checked tones in dialects
|
297
|
+
*/
|
298
|
+
zhuyin: {
|
299
|
+
base: '[\u3105-\u312D\u31A0-\u31BA]',
|
300
|
+
initial: '[\u3105-\u3119\u312A-\u312C\u31A0-\u31A3]',
|
301
|
+
medial: '[\u3127-\u3129]',
|
302
|
+
final: '[\u311A-\u3129\u312D\u31A4-\u31B3\u31B8-\u31BA]',
|
303
|
+
tone: '[\u02D9\u02CA\u02C5\u02C7\u02CB\u02EA\u02EB]',
|
304
|
+
ruyun: '[\u31B4-\u31B7][\u0358\u030d]?'
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
308
|
+
var TYPESET = (function() {
|
309
|
+
var rWhite = '[\\x20\\t\\r\\n\\f]'
|
310
|
+
// Whitespace characters
|
311
|
+
// http://www.w3.org/TR/css3-selectors/#whitespace
|
312
|
+
|
313
|
+
var rPtOpen = UNICODE.punct.open
|
314
|
+
var rPtClose = UNICODE.punct.close
|
315
|
+
var rPtEnd = UNICODE.punct.end
|
316
|
+
var rPtMid = UNICODE.punct.middle
|
317
|
+
var rPtSing = UNICODE.punct.sing
|
318
|
+
var rPt = rPtOpen + '|' + rPtEnd + '|' + rPtMid
|
319
|
+
|
320
|
+
var rBdOpen = UNICODE.biaodian.open
|
321
|
+
var rBdClose = UNICODE.biaodian.close
|
322
|
+
var rBdEnd = UNICODE.biaodian.end
|
323
|
+
var rBdMid = UNICODE.biaodian.middle
|
324
|
+
var rBdLiga = UNICODE.biaodian.liga + '{2}'
|
325
|
+
var rBd = rBdOpen + '|' + rBdEnd + '|' + rBdMid
|
326
|
+
|
327
|
+
var rKana = UNICODE.kana.base + UNICODE.kana.combine + '?'
|
328
|
+
var rKanaS = UNICODE.kana.small + UNICODE.kana.combine + '?'
|
329
|
+
var rKanaH = UNICODE.kana.half
|
330
|
+
var rEon = UNICODE.eonmun.base + '|' + UNICODE.eonmun.letter
|
331
|
+
var rEonH = UNICODE.eonmun.half
|
332
|
+
|
333
|
+
var rHan = UNICODE.hanzi.base + '|' + UNICODE.hanzi.desc + '|' + UNICODE.hanzi.radical + '|' + rKana
|
334
|
+
|
335
|
+
var rCbn = UNICODE.ellinika.combine
|
336
|
+
var rLatn = UNICODE.latin.base + rCbn + '*'
|
337
|
+
var rGk = UNICODE.ellinika.base + rCbn + '*'
|
338
|
+
|
339
|
+
var rCyCbn = UNICODE.kirillica.combine
|
340
|
+
var rCy = UNICODE.kirillica.base + rCyCbn + '*'
|
341
|
+
|
342
|
+
var rAlph = rLatn + '|' + rGk + '|' + rCy
|
343
|
+
|
344
|
+
// For words like `it's`, `Jones’s` or `'99`
|
345
|
+
var rApo = '[\u0027\u2019]'
|
346
|
+
var rChar = rHan + '|(' + rAlph + '|' + rApo + ')+'
|
347
|
+
|
348
|
+
var rZyS = UNICODE.zhuyin.initial
|
349
|
+
var rZyJ = UNICODE.zhuyin.medial
|
350
|
+
var rZyY = UNICODE.zhuyin.final
|
351
|
+
var rZyD = UNICODE.zhuyin.tone + '|' + UNICODE.zhuyin.ruyun
|
352
|
+
|
353
|
+
return {
|
354
|
+
/* Character-level selector (字級選擇器)
|
355
|
+
*/
|
356
|
+
char: {
|
357
|
+
punct: {
|
358
|
+
all: new RegExp( '(' + rPt + ')', 'g' ),
|
359
|
+
open: new RegExp( '(' + rPtOpen + ')', 'g' ),
|
360
|
+
end: new RegExp( '(' + rPtEnd + ')', 'g' ),
|
361
|
+
sing: new RegExp( '(' + rPtSing + ')', 'g' )
|
362
|
+
},
|
363
|
+
|
364
|
+
biaodian: {
|
365
|
+
all: new RegExp( '(' + rBd + ')', 'g' ),
|
366
|
+
open: new RegExp( '(' + rBdOpen + ')', 'g' ),
|
367
|
+
close: new RegExp( '(' + rBdClose + ')', 'g' ),
|
368
|
+
end: new RegExp( '(' + rBdEnd + ')', 'g' ),
|
369
|
+
liga: new RegExp( '(' + rBdLiga + ')', 'g' ),
|
370
|
+
|
371
|
+
group: [
|
372
|
+
new RegExp( '(' + rBdOpen + '|' + rBdMid + '|' + rBdEnd + '){2,}', 'g' ),
|
373
|
+
new RegExp( '(' + rBdLiga + rBdOpen + ')', 'g' )
|
374
|
+
]
|
375
|
+
},
|
376
|
+
|
377
|
+
hanzi: {
|
378
|
+
individual: new RegExp( '(' + rHan + ')', 'g' ),
|
379
|
+
group: new RegExp( '(' + rHan + ')+', 'g' )
|
380
|
+
},
|
381
|
+
|
382
|
+
word: new RegExp( '(' + rLatn + '|' + rGk + '|' + rCy + '|' + rPt + ')+', 'ig' ),
|
383
|
+
|
384
|
+
alphabet: {
|
385
|
+
latin: new RegExp( '(' + rLatn + ')', 'ig' ),
|
386
|
+
ellinika: new RegExp( '(' + rGk + ')', 'ig' ),
|
387
|
+
kirillica: new RegExp( '(' + rCy + ')', 'ig' ),
|
388
|
+
kana: new RegExp( '(' + rKana + ')', 'g' ),
|
389
|
+
smallkana: new RegExp( '(' + rKanaS + ')', 'g' ),
|
390
|
+
eonmun: new RegExp( '(' + rEon + ')', 'g' ),
|
391
|
+
halfeonmun: new RegExp( '(' + rEonH + ')', 'g' )
|
392
|
+
}
|
393
|
+
},
|
394
|
+
|
395
|
+
/* Punctuation Rules (禁則)
|
396
|
+
*/
|
397
|
+
jinze: {
|
398
|
+
touwei: new RegExp( '(' + rBdOpen + '+)(' + rChar + ')(' + rBdEnd + '+)', 'ig' ),
|
399
|
+
tou: new RegExp( '(' + rBdOpen + '+)(' + rChar + ')', 'ig' ),
|
400
|
+
wei: new RegExp( '(' + rChar + ')(' + rBdEnd + '+)', 'ig' ),
|
401
|
+
middle: new RegExp( '(' + rChar + ')(' + rBdMid + ')(' + rChar + ')', 'ig' )
|
402
|
+
},
|
403
|
+
|
404
|
+
zhuyin: {
|
405
|
+
form: new RegExp( '^\u02D9?(' + rZyS + ')?(' + rZyJ + ')?(' + rZyY + ')?(' + rZyD + ')?$' ),
|
406
|
+
diao: new RegExp( '(' + rZyD + ')', 'g' )
|
407
|
+
},
|
408
|
+
|
409
|
+
/* Hanzi and Western mixed spacing (漢字西文混排間隙)
|
410
|
+
* - Basic mode
|
411
|
+
* - Strict mode
|
412
|
+
*/
|
413
|
+
hws: {
|
414
|
+
base: [
|
415
|
+
new RegExp( '('+ rHan +')(' + rAlph + '|' + rPtOpen + ')', 'ig' ),
|
416
|
+
new RegExp( '('+ rAlph+ '|' + rPtEnd +')(' + rHan + ')', 'ig' )
|
417
|
+
],
|
418
|
+
|
419
|
+
strict: [
|
420
|
+
new RegExp( '('+ rHan +')' + rWhite + '?(' + rAlph + '|' + rPtOpen + ')', 'ig' ),
|
421
|
+
new RegExp( '('+ rAlph+ '|' + rPtEnd +')' + rWhite + '?(' + rHan + ')', 'ig' )
|
422
|
+
]
|
423
|
+
},
|
424
|
+
|
425
|
+
// The feature displays the following characters
|
426
|
+
// in its variant form for font consistency and
|
427
|
+
// presentational reason. Meanwhile, this won't
|
428
|
+
// alter the original character in the DOM.
|
429
|
+
'display-as': {
|
430
|
+
'ja-font-for-hant': [
|
431
|
+
// '夠 够',
|
432
|
+
'查 査',
|
433
|
+
'啟 啓',
|
434
|
+
'鄉 鄕',
|
435
|
+
'值 値',
|
436
|
+
'污 汚'
|
437
|
+
],
|
438
|
+
|
439
|
+
'comb-liga-pua': [
|
440
|
+
[ '\u0061[\u030d\u0358]', '\uDB80\uDC61' ],
|
441
|
+
[ '\u0065[\u030d\u0358]', '\uDB80\uDC65' ],
|
442
|
+
[ '\u0069[\u030d\u0358]', '\uDB80\uDC69' ],
|
443
|
+
[ '\u006F[\u030d\u0358]', '\uDB80\uDC6F' ],
|
444
|
+
[ '\u0075[\u030d\u0358]', '\uDB80\uDC75' ],
|
445
|
+
|
446
|
+
[ '\u31B4[\u030d\u0358]', '\uDB8C\uDDB4' ],
|
447
|
+
[ '\u31B5[\u030d\u0358]', '\uDB8C\uDDB5' ],
|
448
|
+
[ '\u31B6[\u030d\u0358]', '\uDB8C\uDDB6' ],
|
449
|
+
[ '\u31B7[\u030d\u0358]', '\uDB8C\uDDB7' ]
|
450
|
+
]
|
451
|
+
},
|
452
|
+
|
453
|
+
// The feature actually *converts* the character
|
454
|
+
// in the DOM for semantic reason.
|
455
|
+
//
|
456
|
+
// Note that this could be aggressive.
|
457
|
+
'inaccurate-char': [
|
458
|
+
[ '[\u2022\u2027]', '\u00B7' ],
|
459
|
+
[ '\u22EF\u22EF', '\u2026\u2026' ],
|
460
|
+
[ '\u2500\u2500', '\u2014\u2014' ],
|
461
|
+
[ '\u2035', '\u2018' ],
|
462
|
+
[ '\u2032', '\u2019' ],
|
463
|
+
[ '\u2036', '\u201C' ],
|
464
|
+
[ '\u2033', '\u201D' ]
|
465
|
+
]
|
466
|
+
}
|
467
|
+
})()
|
468
|
+
|
469
|
+
Han.UNICODE = UNICODE
|
470
|
+
Han.TYPESET = TYPESET
|
471
|
+
|
472
|
+
// Aliases
|
473
|
+
Han.UNICODE.cjk = Han.UNICODE.hanzi
|
474
|
+
Han.UNICODE.greek = Han.UNICODE.ellinika
|
475
|
+
Han.UNICODE.cyrillic = Han.UNICODE.kirillica
|
476
|
+
Han.UNICODE.hangul = Han.UNICODE.eonmun
|
477
|
+
|
478
|
+
Han.TYPESET.char.cjk = Han.TYPESET.char.hanzi
|
479
|
+
Han.TYPESET.char.alphabet.greek = Han.TYPESET.char.alphabet.ellinika
|
480
|
+
Han.TYPESET.char.alphabet.cyrillic = Han.TYPESET.char.alphabet.kirillica
|
481
|
+
Han.TYPESET.char.alphabet.hangul = Han.TYPESET.char.alphabet.eonmun
|
482
|
+
|
483
|
+
var $ = {
|
484
|
+
// Simplified query selectors which return the node list
|
485
|
+
// in an array
|
486
|
+
id: function( selector, context ) {
|
487
|
+
return ( context || document ).getElementById( selector )
|
488
|
+
},
|
489
|
+
|
490
|
+
tag: function( selector, context ) {
|
491
|
+
return this.makeArray(
|
492
|
+
( context || document ).getElementsByTagName( selector )
|
493
|
+
)
|
494
|
+
},
|
495
|
+
|
496
|
+
qsa: function( selector, context ) {
|
497
|
+
return this.makeArray(
|
498
|
+
( context || document ).querySelectorAll( selector )
|
499
|
+
)
|
500
|
+
},
|
501
|
+
|
502
|
+
// Create a document fragment, a text node with text
|
503
|
+
// or an element with/without classes
|
504
|
+
create: function( elem, clazz ) {
|
505
|
+
var elem = '!' === elem ?
|
506
|
+
document.createDocumentFragment() :
|
507
|
+
'' === elem ?
|
508
|
+
document.createTextNode( clazz || '' ) :
|
509
|
+
document.createElement( elem )
|
510
|
+
|
511
|
+
try {
|
512
|
+
if ( clazz ) {
|
513
|
+
elem.className = clazz
|
514
|
+
}
|
515
|
+
} catch (e) {}
|
516
|
+
|
517
|
+
return elem
|
518
|
+
},
|
519
|
+
|
520
|
+
// Clone a node (text, element or fragment) deeply or
|
521
|
+
// childlessly
|
522
|
+
clone: function( node, deep ) {
|
523
|
+
return node.cloneNode( deep || true )
|
524
|
+
},
|
525
|
+
|
526
|
+
// Remove a node (text, element or fragment)
|
527
|
+
remove: function( node, parent ) {
|
528
|
+
return ( parent || node.parentNode ).removeChild( node )
|
529
|
+
},
|
530
|
+
|
531
|
+
// Set attributes all in once with an object
|
532
|
+
setAttr: function( target, attr ) {
|
533
|
+
if ( typeof attr !== 'object' ) return
|
534
|
+
var len = attr.length
|
535
|
+
|
536
|
+
// Native NamedNodeMap
|
537
|
+
if ( typeof attr[ 0 ] === 'object' && 'name' in attr[ 0 ] ) {
|
538
|
+
for ( var i = 0; i < len; i++ ) {
|
539
|
+
if ( attr[ i ].value !== undefined ) {
|
540
|
+
target.setAttribute( attr[ i ].name, attr[ i ].value )
|
541
|
+
}
|
542
|
+
}
|
543
|
+
|
544
|
+
// Plain object
|
545
|
+
} else {
|
546
|
+
for ( var name in attr ) {
|
547
|
+
if ( attr.hasOwnProperty( name ) && attr[ name ] !== undefined ) {
|
548
|
+
target.setAttribute( name, attr[ name ] )
|
549
|
+
}
|
550
|
+
}
|
551
|
+
}
|
552
|
+
return target
|
553
|
+
},
|
554
|
+
|
555
|
+
// Return if the current node should be ignored,
|
556
|
+
// `<wbr>` or comments
|
557
|
+
isIgnorable: function( node ) {
|
558
|
+
return node.nodeName === 'WBR' || node.nodeType === Node.COMMENT_NODE
|
559
|
+
},
|
560
|
+
|
561
|
+
// Convert array-like objects into real arrays
|
562
|
+
// for the native prototype methods
|
563
|
+
makeArray: function( obj ) {
|
564
|
+
return Array.prototype.slice.call( obj )
|
565
|
+
},
|
566
|
+
|
567
|
+
// Extend target with an object
|
568
|
+
extend: function( target, object ) {
|
569
|
+
var isExtensible = typeof target === 'object' ||
|
570
|
+
typeof target === 'function' ||
|
571
|
+
typeof object === 'object'
|
572
|
+
|
573
|
+
if ( !isExtensible ) return
|
574
|
+
|
575
|
+
for ( var name in object ) {
|
576
|
+
if ( object.hasOwnProperty( name )) {
|
577
|
+
target[ name ] = object[ name ]
|
578
|
+
}
|
579
|
+
}
|
580
|
+
return target
|
581
|
+
}
|
582
|
+
}
|
583
|
+
|
584
|
+
var Fibre =
|
585
|
+
/*!
|
586
|
+
* Fibre.js v0.1.2 | MIT License | github.com/ethantw/fibre.js
|
587
|
+
* Based on findAndReplaceDOMText
|
588
|
+
*/
|
589
|
+
|
590
|
+
function( Finder ) {
|
591
|
+
|
592
|
+
'use strict'
|
593
|
+
|
594
|
+
var VERSION = '0.1.2'
|
595
|
+
var FILTER_OUT_SELECTOR = 'style, script, head title'
|
596
|
+
|
597
|
+
var global = window || {}
|
598
|
+
var document = global.document || undefined
|
599
|
+
|
600
|
+
function matches( node, selector, bypassNodeType39 ) {
|
601
|
+
var Efn = Element.prototype
|
602
|
+
var matches = Efn.matches || Efn.mozMatchesSelector || Efn.msMatchesSelector || Efn.webkitMatchesSelector
|
603
|
+
|
604
|
+
if ( node instanceof Element ) {
|
605
|
+
return matches.call( node, selector )
|
606
|
+
} else if ( bypassNodeType39 ) {
|
607
|
+
if ( /^[39]$/.test( node.nodeType )) return true
|
608
|
+
}
|
609
|
+
return false
|
610
|
+
}
|
611
|
+
|
612
|
+
if ( typeof document === 'undefined' ) throw new Error( 'Fibre requires a DOM-supported environment.' )
|
613
|
+
|
614
|
+
var Fibre = function( context ) {
|
615
|
+
return new Fibre.fn.init( context )
|
616
|
+
}
|
617
|
+
|
618
|
+
Fibre.version = VERSION
|
619
|
+
Fibre.matches = matches
|
620
|
+
|
621
|
+
Fibre.fn = Fibre.prototype = {
|
622
|
+
constructor: Fibre,
|
623
|
+
|
624
|
+
version: VERSION,
|
625
|
+
|
626
|
+
context: undefined,
|
627
|
+
|
628
|
+
contextSelector: null,
|
629
|
+
|
630
|
+
finder: [],
|
631
|
+
|
632
|
+
init: function( context ) {
|
633
|
+
if ( !context ) throw new Error( 'A context is required for Fibre to initialise.' )
|
634
|
+
|
635
|
+
if ( context instanceof Node ) {
|
636
|
+
this.context = context
|
637
|
+
} else if ( typeof context === 'string' ) {
|
638
|
+
this.contextSelector = context
|
639
|
+
this.context = document.querySelector( context )
|
640
|
+
}
|
641
|
+
|
642
|
+
return this
|
643
|
+
},
|
644
|
+
|
645
|
+
filterElemFn: function( currentNode ) {
|
646
|
+
return matches( currentNode, this.filterSelector, true ) &&
|
647
|
+
!matches( currentNode, this.filterOutSelector )
|
648
|
+
},
|
649
|
+
|
650
|
+
filterSelector: '*',
|
651
|
+
|
652
|
+
filter: function( selector ) {
|
653
|
+
switch ( typeof selector ) {
|
654
|
+
case 'string':
|
655
|
+
this.filterSelector = selector
|
656
|
+
break
|
657
|
+
case 'function':
|
658
|
+
this.filterElemFn = selector
|
659
|
+
break
|
660
|
+
default:
|
661
|
+
return this
|
662
|
+
}
|
663
|
+
return this
|
664
|
+
},
|
665
|
+
|
666
|
+
filterOutSelector: FILTER_OUT_SELECTOR,
|
667
|
+
|
668
|
+
filterOut: function( selector, boolExtend ) {
|
669
|
+
switch( typeof selector ) {
|
670
|
+
case 'string':
|
671
|
+
if ( typeof boolExtend !== 'undefined' && boolExtend === true ) {
|
672
|
+
this.filterOutSelector += ', ' + selector
|
673
|
+
} else {
|
674
|
+
this.filterOutSelector = selector
|
675
|
+
}
|
676
|
+
break
|
677
|
+
default:
|
678
|
+
return this
|
679
|
+
}
|
680
|
+
return this
|
681
|
+
},
|
682
|
+
|
683
|
+
replace: function( regexp, newSubStr, portionMode ) {
|
684
|
+
var it = this
|
685
|
+
var portionMode = portionMode || 'retain'
|
686
|
+
it.finder.push(Finder( it.context, {
|
687
|
+
find: regexp,
|
688
|
+
replace: newSubStr,
|
689
|
+
filterElements: function( currentNode ) {
|
690
|
+
return it.filterElemFn( currentNode )
|
691
|
+
},
|
692
|
+
portionMode: portionMode
|
693
|
+
}))
|
694
|
+
return it
|
695
|
+
},
|
696
|
+
|
697
|
+
wrap: function( regexp, strElemName, portionMode ) {
|
698
|
+
var it = this
|
699
|
+
var portionMode = portionMode || 'retain'
|
700
|
+
it.finder.push(Finder( it.context, {
|
701
|
+
find: regexp,
|
702
|
+
wrap: strElemName,
|
703
|
+
filterElements: function( currentNode ) {
|
704
|
+
return it.filterElemFn( currentNode )
|
705
|
+
},
|
706
|
+
portionMode: portionMode
|
707
|
+
}))
|
708
|
+
return it
|
709
|
+
},
|
710
|
+
|
711
|
+
revert: function( level ) {
|
712
|
+
var max = this.finder.length
|
713
|
+
var level = Number( level ) || ( level === 0 ? Number(0) :
|
714
|
+
( level === 'all' ? max : 1 ))
|
715
|
+
|
716
|
+
if ( typeof max === 'undefined' || max === 0 ) return this
|
717
|
+
else if ( level > max ) level = max
|
718
|
+
|
719
|
+
for ( var i = level; i > 0; i-- ) {
|
720
|
+
this.finder.pop().revert()
|
721
|
+
}
|
722
|
+
return this
|
723
|
+
}
|
724
|
+
}
|
725
|
+
|
726
|
+
Fibre.fn.init.prototype = Fibre.fn
|
727
|
+
|
728
|
+
return Fibre
|
729
|
+
|
730
|
+
}(
|
731
|
+
|
732
|
+
/**
|
733
|
+
* findAndReplaceDOMText v 0.4.2
|
734
|
+
* @author James Padolsey http://james.padolsey.com
|
735
|
+
* @license http://unlicense.org/UNLICENSE
|
736
|
+
*
|
737
|
+
* Matches the text of a DOM node against a regular expression
|
738
|
+
* and replaces each match (or node-separated portions of the match)
|
739
|
+
* in the specified element.
|
740
|
+
*/
|
741
|
+
(function() {
|
742
|
+
|
743
|
+
var PORTION_MODE_RETAIN = 'retain'
|
744
|
+
var PORTION_MODE_FIRST = 'first'
|
745
|
+
var doc = document
|
746
|
+
var toString = {}.toString
|
747
|
+
function isArray(a) {
|
748
|
+
return toString.call(a) == '[object Array]'
|
749
|
+
}
|
750
|
+
|
751
|
+
function escapeRegExp(s) {
|
752
|
+
return String(s).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1')
|
753
|
+
}
|
754
|
+
|
755
|
+
function exposed() {
|
756
|
+
// Try deprecated arg signature first:
|
757
|
+
return deprecated.apply(null, arguments) || findAndReplaceDOMText.apply(null, arguments)
|
758
|
+
}
|
759
|
+
|
760
|
+
function deprecated(regex, node, replacement, captureGroup, elFilter) {
|
761
|
+
if ((node && !node.nodeType) && arguments.length <= 2) {
|
762
|
+
return false
|
763
|
+
}
|
764
|
+
var isReplacementFunction = typeof replacement == 'function'
|
765
|
+
if (isReplacementFunction) {
|
766
|
+
replacement = (function(original) {
|
767
|
+
return function(portion, match) {
|
768
|
+
return original(portion.text, match.startIndex)
|
769
|
+
}
|
770
|
+
}(replacement))
|
771
|
+
}
|
772
|
+
|
773
|
+
// Awkward support for deprecated argument signature (<0.4.0)
|
774
|
+
var instance = findAndReplaceDOMText(node, {
|
775
|
+
|
776
|
+
find: regex,
|
777
|
+
|
778
|
+
wrap: isReplacementFunction ? null : replacement,
|
779
|
+
replace: isReplacementFunction ? replacement : '$' + (captureGroup || '&'),
|
780
|
+
|
781
|
+
prepMatch: function(m, mi) {
|
782
|
+
|
783
|
+
// Support captureGroup (a deprecated feature)
|
784
|
+
|
785
|
+
if (!m[0]) throw 'findAndReplaceDOMText cannot handle zero-length matches'
|
786
|
+
if (captureGroup > 0) {
|
787
|
+
var cg = m[captureGroup]
|
788
|
+
m.index += m[0].indexOf(cg)
|
789
|
+
m[0] = cg
|
790
|
+
}
|
791
|
+
|
792
|
+
m.endIndex = m.index + m[0].length
|
793
|
+
m.startIndex = m.index
|
794
|
+
m.index = mi
|
795
|
+
return m
|
796
|
+
},
|
797
|
+
filterElements: elFilter
|
798
|
+
})
|
799
|
+
exposed.revert = function() {
|
800
|
+
return instance.revert()
|
801
|
+
}
|
802
|
+
return true
|
803
|
+
}
|
804
|
+
|
805
|
+
/**
|
806
|
+
* findAndReplaceDOMText
|
807
|
+
*
|
808
|
+
* Locates matches and replaces with replacementNode
|
809
|
+
*
|
810
|
+
* @param {Node} node Element or Text node to search within
|
811
|
+
* @param {RegExp} options.find The regular expression to match
|
812
|
+
* @param {String|Element} [options.wrap] A NodeName, or a Node to clone
|
813
|
+
* @param {String|Function} [options.replace='$&'] What to replace each match with
|
814
|
+
* @param {Function} [options.filterElements] A Function to be called to check whether to
|
815
|
+
* process an element. (returning true = process element,
|
816
|
+
* returning false = avoid element)
|
817
|
+
*/
|
818
|
+
function findAndReplaceDOMText(node, options) {
|
819
|
+
return new Finder(node, options)
|
820
|
+
}
|
821
|
+
|
822
|
+
exposed.Finder = Finder
|
823
|
+
/**
|
824
|
+
* Finder -- encapsulates logic to find and replace.
|
825
|
+
*/
|
826
|
+
function Finder(node, options) {
|
827
|
+
|
828
|
+
options.portionMode = options.portionMode || PORTION_MODE_RETAIN
|
829
|
+
this.node = node
|
830
|
+
this.options = options
|
831
|
+
// ENable match-preparation method to be passed as option:
|
832
|
+
this.prepMatch = options.prepMatch || this.prepMatch
|
833
|
+
this.reverts = []
|
834
|
+
this.matches = this.search()
|
835
|
+
if (this.matches.length) {
|
836
|
+
this.processMatches()
|
837
|
+
}
|
838
|
+
|
839
|
+
}
|
840
|
+
|
841
|
+
Finder.prototype = {
|
842
|
+
|
843
|
+
/**
|
844
|
+
* Searches for all matches that comply with the instance's 'match' option
|
845
|
+
*/
|
846
|
+
search: function() {
|
847
|
+
|
848
|
+
var match
|
849
|
+
var matchIndex = 0
|
850
|
+
var regex = this.options.find
|
851
|
+
var text = this.getAggregateText()
|
852
|
+
var matches = []
|
853
|
+
regex = typeof regex === 'string' ? RegExp(escapeRegExp(regex), 'g') : regex
|
854
|
+
if (regex.global) {
|
855
|
+
while (match = regex.exec(text)) {
|
856
|
+
matches.push(this.prepMatch(match, matchIndex++))
|
857
|
+
}
|
858
|
+
} else {
|
859
|
+
if (match = text.match(regex)) {
|
860
|
+
matches.push(this.prepMatch(match, 0))
|
861
|
+
}
|
862
|
+
}
|
863
|
+
|
864
|
+
return matches
|
865
|
+
},
|
866
|
+
|
867
|
+
/**
|
868
|
+
* Prepares a single match with useful meta info:
|
869
|
+
*/
|
870
|
+
prepMatch: function(match, matchIndex) {
|
871
|
+
|
872
|
+
if (!match[0]) {
|
873
|
+
throw new Error('findAndReplaceDOMText cannot handle zero-length matches')
|
874
|
+
}
|
875
|
+
|
876
|
+
match.endIndex = match.index + match[0].length
|
877
|
+
match.startIndex = match.index
|
878
|
+
match.index = matchIndex
|
879
|
+
return match
|
880
|
+
},
|
881
|
+
|
882
|
+
/**
|
883
|
+
* Gets aggregate text within subject node
|
884
|
+
*/
|
885
|
+
getAggregateText: function() {
|
886
|
+
|
887
|
+
var elementFilter = this.options.filterElements
|
888
|
+
return getText(this.node)
|
889
|
+
/**
|
890
|
+
* Gets aggregate text of a node without resorting
|
891
|
+
* to broken innerText/textContent
|
892
|
+
*/
|
893
|
+
function getText(node) {
|
894
|
+
|
895
|
+
if (node.nodeType === 3) {
|
896
|
+
return node.data
|
897
|
+
}
|
898
|
+
|
899
|
+
if (elementFilter && !elementFilter(node)) {
|
900
|
+
return ''
|
901
|
+
}
|
902
|
+
|
903
|
+
var txt = ''
|
904
|
+
if (node = node.firstChild) do {
|
905
|
+
txt += getText(node)
|
906
|
+
} while (node = node.nextSibling)
|
907
|
+
return txt
|
908
|
+
}
|
909
|
+
|
910
|
+
},
|
911
|
+
|
912
|
+
/**
|
913
|
+
* Steps through the target node, looking for matches, and
|
914
|
+
* calling replaceFn when a match is found.
|
915
|
+
*/
|
916
|
+
processMatches: function() {
|
917
|
+
|
918
|
+
var matches = this.matches
|
919
|
+
var node = this.node
|
920
|
+
var elementFilter = this.options.filterElements
|
921
|
+
var startPortion,
|
922
|
+
endPortion,
|
923
|
+
innerPortions = [],
|
924
|
+
curNode = node,
|
925
|
+
match = matches.shift(),
|
926
|
+
atIndex = 0, // i.e. nodeAtIndex
|
927
|
+
matchIndex = 0,
|
928
|
+
portionIndex = 0,
|
929
|
+
doAvoidNode,
|
930
|
+
nodeStack = [node]
|
931
|
+
out: while (true) {
|
932
|
+
|
933
|
+
if (curNode.nodeType === 3) {
|
934
|
+
|
935
|
+
if (!endPortion && curNode.length + atIndex >= match.endIndex) {
|
936
|
+
|
937
|
+
// We've found the ending
|
938
|
+
endPortion = {
|
939
|
+
node: curNode,
|
940
|
+
index: portionIndex++,
|
941
|
+
text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex),
|
942
|
+
indexInMatch: atIndex - match.startIndex,
|
943
|
+
indexInNode: match.startIndex - atIndex, // always zero for end-portions
|
944
|
+
endIndexInNode: match.endIndex - atIndex,
|
945
|
+
isEnd: true
|
946
|
+
}
|
947
|
+
} else if (startPortion) {
|
948
|
+
// Intersecting node
|
949
|
+
innerPortions.push({
|
950
|
+
node: curNode,
|
951
|
+
index: portionIndex++,
|
952
|
+
text: curNode.data,
|
953
|
+
indexInMatch: atIndex - match.startIndex,
|
954
|
+
indexInNode: 0 // always zero for inner-portions
|
955
|
+
})
|
956
|
+
}
|
957
|
+
|
958
|
+
if (!startPortion && curNode.length + atIndex > match.startIndex) {
|
959
|
+
// We've found the match start
|
960
|
+
startPortion = {
|
961
|
+
node: curNode,
|
962
|
+
index: portionIndex++,
|
963
|
+
indexInMatch: 0,
|
964
|
+
indexInNode: match.startIndex - atIndex,
|
965
|
+
endIndexInNode: match.endIndex - atIndex,
|
966
|
+
text: curNode.data.substring(match.startIndex - atIndex, match.endIndex - atIndex)
|
967
|
+
}
|
968
|
+
}
|
969
|
+
|
970
|
+
atIndex += curNode.data.length
|
971
|
+
}
|
972
|
+
|
973
|
+
doAvoidNode = curNode.nodeType === 1 && elementFilter && !elementFilter(curNode)
|
974
|
+
if (startPortion && endPortion) {
|
975
|
+
|
976
|
+
curNode = this.replaceMatch(match, startPortion, innerPortions, endPortion)
|
977
|
+
// processMatches has to return the node that replaced the endNode
|
978
|
+
// and then we step back so we can continue from the end of the
|
979
|
+
// match:
|
980
|
+
|
981
|
+
atIndex -= (endPortion.node.data.length - endPortion.endIndexInNode)
|
982
|
+
startPortion = null
|
983
|
+
endPortion = null
|
984
|
+
innerPortions = []
|
985
|
+
match = matches.shift()
|
986
|
+
portionIndex = 0
|
987
|
+
matchIndex++
|
988
|
+
if (!match) {
|
989
|
+
break; // no more matches
|
990
|
+
}
|
991
|
+
|
992
|
+
} else if (
|
993
|
+
!doAvoidNode &&
|
994
|
+
(curNode.firstChild || curNode.nextSibling)
|
995
|
+
) {
|
996
|
+
// Move down or forward:
|
997
|
+
if (curNode.firstChild) {
|
998
|
+
nodeStack.push(curNode)
|
999
|
+
curNode = curNode.firstChild
|
1000
|
+
} else {
|
1001
|
+
curNode = curNode.nextSibling
|
1002
|
+
}
|
1003
|
+
continue
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
// Move forward or up:
|
1007
|
+
while (true) {
|
1008
|
+
if (curNode.nextSibling) {
|
1009
|
+
curNode = curNode.nextSibling
|
1010
|
+
break
|
1011
|
+
}
|
1012
|
+
curNode = nodeStack.pop()
|
1013
|
+
if (curNode === node) {
|
1014
|
+
break out
|
1015
|
+
}
|
1016
|
+
}
|
1017
|
+
|
1018
|
+
}
|
1019
|
+
|
1020
|
+
},
|
1021
|
+
|
1022
|
+
/**
|
1023
|
+
* Reverts ... TODO
|
1024
|
+
*/
|
1025
|
+
revert: function() {
|
1026
|
+
// Reversion occurs backwards so as to avoid nodes subsequently
|
1027
|
+
// replaced during the matching phase (a forward process):
|
1028
|
+
for (var l = this.reverts.length; l--;) {
|
1029
|
+
this.reverts[l]()
|
1030
|
+
}
|
1031
|
+
this.reverts = []
|
1032
|
+
},
|
1033
|
+
|
1034
|
+
prepareReplacementString: function(string, portion, match, matchIndex) {
|
1035
|
+
var portionMode = this.options.portionMode
|
1036
|
+
if (
|
1037
|
+
portionMode === PORTION_MODE_FIRST &&
|
1038
|
+
portion.indexInMatch > 0
|
1039
|
+
) {
|
1040
|
+
return ''
|
1041
|
+
}
|
1042
|
+
string = string.replace(/\$(\d+|&|`|')/g, function($0, t) {
|
1043
|
+
var replacement
|
1044
|
+
switch(t) {
|
1045
|
+
case '&':
|
1046
|
+
replacement = match[0]
|
1047
|
+
break
|
1048
|
+
case '`':
|
1049
|
+
replacement = match.input.substring(0, match.startIndex)
|
1050
|
+
break
|
1051
|
+
case '\'':
|
1052
|
+
replacement = match.input.substring(match.endIndex)
|
1053
|
+
break
|
1054
|
+
default:
|
1055
|
+
replacement = match[+t]
|
1056
|
+
}
|
1057
|
+
return replacement
|
1058
|
+
})
|
1059
|
+
if (portionMode === PORTION_MODE_FIRST) {
|
1060
|
+
return string
|
1061
|
+
}
|
1062
|
+
|
1063
|
+
if (portion.isEnd) {
|
1064
|
+
return string.substring(portion.indexInMatch)
|
1065
|
+
}
|
1066
|
+
|
1067
|
+
return string.substring(portion.indexInMatch, portion.indexInMatch + portion.text.length)
|
1068
|
+
},
|
1069
|
+
|
1070
|
+
getPortionReplacementNode: function(portion, match, matchIndex) {
|
1071
|
+
|
1072
|
+
var replacement = this.options.replace || '$&'
|
1073
|
+
var wrapper = this.options.wrap
|
1074
|
+
if (wrapper && wrapper.nodeType) {
|
1075
|
+
// Wrapper has been provided as a stencil-node for us to clone:
|
1076
|
+
var clone = doc.createElement('div')
|
1077
|
+
clone.innerHTML = wrapper.outerHTML || new XMLSerializer().serializeToString(wrapper)
|
1078
|
+
wrapper = clone.firstChild
|
1079
|
+
}
|
1080
|
+
|
1081
|
+
if (typeof replacement == 'function') {
|
1082
|
+
replacement = replacement(portion, match, matchIndex)
|
1083
|
+
if (replacement && replacement.nodeType) {
|
1084
|
+
return replacement
|
1085
|
+
}
|
1086
|
+
return doc.createTextNode(String(replacement))
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
var el = typeof wrapper == 'string' ? doc.createElement(wrapper) : wrapper
|
1090
|
+
replacement = doc.createTextNode(
|
1091
|
+
this.prepareReplacementString(
|
1092
|
+
replacement, portion, match, matchIndex
|
1093
|
+
)
|
1094
|
+
)
|
1095
|
+
if (!replacement.data) {
|
1096
|
+
return replacement
|
1097
|
+
}
|
1098
|
+
|
1099
|
+
if (!el) {
|
1100
|
+
return replacement
|
1101
|
+
}
|
1102
|
+
|
1103
|
+
el.appendChild(replacement)
|
1104
|
+
return el
|
1105
|
+
},
|
1106
|
+
|
1107
|
+
replaceMatch: function(match, startPortion, innerPortions, endPortion) {
|
1108
|
+
|
1109
|
+
var matchStartNode = startPortion.node
|
1110
|
+
var matchEndNode = endPortion.node
|
1111
|
+
var preceedingTextNode
|
1112
|
+
var followingTextNode
|
1113
|
+
if (matchStartNode === matchEndNode) {
|
1114
|
+
|
1115
|
+
var node = matchStartNode
|
1116
|
+
if (startPortion.indexInNode > 0) {
|
1117
|
+
// Add `before` text node (before the match)
|
1118
|
+
preceedingTextNode = doc.createTextNode(node.data.substring(0, startPortion.indexInNode))
|
1119
|
+
node.parentNode.insertBefore(preceedingTextNode, node)
|
1120
|
+
}
|
1121
|
+
|
1122
|
+
// Create the replacement node:
|
1123
|
+
var newNode = this.getPortionReplacementNode(
|
1124
|
+
endPortion,
|
1125
|
+
match
|
1126
|
+
)
|
1127
|
+
node.parentNode.insertBefore(newNode, node)
|
1128
|
+
if (endPortion.endIndexInNode < node.length) { // ?????
|
1129
|
+
// Add `after` text node (after the match)
|
1130
|
+
followingTextNode = doc.createTextNode(node.data.substring(endPortion.endIndexInNode))
|
1131
|
+
node.parentNode.insertBefore(followingTextNode, node)
|
1132
|
+
}
|
1133
|
+
|
1134
|
+
node.parentNode.removeChild(node)
|
1135
|
+
this.reverts.push(function() {
|
1136
|
+
if (preceedingTextNode === newNode.previousSibling) {
|
1137
|
+
preceedingTextNode.parentNode.removeChild(preceedingTextNode)
|
1138
|
+
}
|
1139
|
+
if (followingTextNode === newNode.nextSibling) {
|
1140
|
+
followingTextNode.parentNode.removeChild(followingTextNode)
|
1141
|
+
}
|
1142
|
+
newNode.parentNode.replaceChild(node, newNode)
|
1143
|
+
})
|
1144
|
+
return newNode
|
1145
|
+
} else {
|
1146
|
+
// Replace matchStartNode -> [innerMatchNodes...] -> matchEndNode (in that order)
|
1147
|
+
|
1148
|
+
preceedingTextNode = doc.createTextNode(
|
1149
|
+
matchStartNode.data.substring(0, startPortion.indexInNode)
|
1150
|
+
)
|
1151
|
+
followingTextNode = doc.createTextNode(
|
1152
|
+
matchEndNode.data.substring(endPortion.endIndexInNode)
|
1153
|
+
)
|
1154
|
+
var firstNode = this.getPortionReplacementNode(
|
1155
|
+
startPortion,
|
1156
|
+
match
|
1157
|
+
)
|
1158
|
+
var innerNodes = []
|
1159
|
+
for (var i = 0, l = innerPortions.length; i < l; ++i) {
|
1160
|
+
var portion = innerPortions[i]
|
1161
|
+
var innerNode = this.getPortionReplacementNode(
|
1162
|
+
portion,
|
1163
|
+
match
|
1164
|
+
)
|
1165
|
+
portion.node.parentNode.replaceChild(innerNode, portion.node)
|
1166
|
+
this.reverts.push((function(portion, innerNode) {
|
1167
|
+
return function() {
|
1168
|
+
innerNode.parentNode.replaceChild(portion.node, innerNode)
|
1169
|
+
}
|
1170
|
+
}(portion, innerNode)))
|
1171
|
+
innerNodes.push(innerNode)
|
1172
|
+
}
|
1173
|
+
|
1174
|
+
var lastNode = this.getPortionReplacementNode(
|
1175
|
+
endPortion,
|
1176
|
+
match
|
1177
|
+
)
|
1178
|
+
matchStartNode.parentNode.insertBefore(preceedingTextNode, matchStartNode)
|
1179
|
+
matchStartNode.parentNode.insertBefore(firstNode, matchStartNode)
|
1180
|
+
matchStartNode.parentNode.removeChild(matchStartNode)
|
1181
|
+
matchEndNode.parentNode.insertBefore(lastNode, matchEndNode)
|
1182
|
+
matchEndNode.parentNode.insertBefore(followingTextNode, matchEndNode)
|
1183
|
+
matchEndNode.parentNode.removeChild(matchEndNode)
|
1184
|
+
this.reverts.push(function() {
|
1185
|
+
preceedingTextNode.parentNode.removeChild(preceedingTextNode)
|
1186
|
+
firstNode.parentNode.replaceChild(matchStartNode, firstNode)
|
1187
|
+
followingTextNode.parentNode.removeChild(followingTextNode)
|
1188
|
+
lastNode.parentNode.replaceChild(matchEndNode, lastNode)
|
1189
|
+
})
|
1190
|
+
return lastNode
|
1191
|
+
}
|
1192
|
+
}
|
1193
|
+
|
1194
|
+
}
|
1195
|
+
return exposed
|
1196
|
+
}())
|
1197
|
+
);
|
1198
|
+
|
1199
|
+
$.extend( Fibre.fn, {
|
1200
|
+
// Force punctuation & biaodian typesetting rules to be applied.
|
1201
|
+
jinzify: function() {
|
1202
|
+
var origFilterOutSelector= this.filterOutSelector
|
1203
|
+
|
1204
|
+
this.filterOutSelector += ', jinze'
|
1205
|
+
|
1206
|
+
this
|
1207
|
+
.replace(
|
1208
|
+
TYPESET.jinze.touwei,
|
1209
|
+
function( portion, match ) {
|
1210
|
+
var mat = match[0]
|
1211
|
+
var text = $.create( '', mat )
|
1212
|
+
var elem = $.create( 'jinze', 'touwei' )
|
1213
|
+
|
1214
|
+
elem.appendChild( text )
|
1215
|
+
return (
|
1216
|
+
( portion.index === 0 && portion.isEnd ) || portion.index === 1
|
1217
|
+
) ? elem : ''
|
1218
|
+
}
|
1219
|
+
)
|
1220
|
+
.replace(
|
1221
|
+
TYPESET.jinze.wei,
|
1222
|
+
function( portion, match ) {
|
1223
|
+
var mat = match[0]
|
1224
|
+
var text = $.create( '', mat )
|
1225
|
+
var elem = $.create( 'jinze', 'wei' )
|
1226
|
+
|
1227
|
+
elem.appendChild( text )
|
1228
|
+
return portion.index === 0 ? elem : ''
|
1229
|
+
}
|
1230
|
+
)
|
1231
|
+
.replace(
|
1232
|
+
TYPESET.jinze.tou,
|
1233
|
+
function( portion, match ) {
|
1234
|
+
var mat = match[0]
|
1235
|
+
var text = $.create( '', mat )
|
1236
|
+
var elem = $.create( 'jinze', 'tou' )
|
1237
|
+
|
1238
|
+
elem.appendChild( text )
|
1239
|
+
return (
|
1240
|
+
( portion.index === 0 && portion.isEnd ) ||
|
1241
|
+
portion.index === 1
|
1242
|
+
) ? elem : ''
|
1243
|
+
}
|
1244
|
+
)
|
1245
|
+
.replace(
|
1246
|
+
TYPESET.jinze.middle,
|
1247
|
+
function( portion, match ) {
|
1248
|
+
var mat = match[0]
|
1249
|
+
var text = $.create( '', mat )
|
1250
|
+
var elem = $.create( 'jinze', 'middle' )
|
1251
|
+
|
1252
|
+
elem.appendChild( text )
|
1253
|
+
return (( portion.index === 0 && portion.isEnd ) || portion.index === 1 )
|
1254
|
+
? elem : ''
|
1255
|
+
}
|
1256
|
+
)
|
1257
|
+
|
1258
|
+
this.filterOutSelector = origFilterOutSelector
|
1259
|
+
return this
|
1260
|
+
},
|
1261
|
+
|
1262
|
+
groupify: function() {
|
1263
|
+
this
|
1264
|
+
.wrap(
|
1265
|
+
TYPESET.char.biaodian.group[ 0 ],
|
1266
|
+
$.clone( $.create( 'char_group', 'biaodian cjk' ))
|
1267
|
+
)
|
1268
|
+
.wrap(
|
1269
|
+
TYPESET.char.biaodian.group[ 1 ],
|
1270
|
+
$.clone( $.create( 'char_group', 'biaodian cjk' ))
|
1271
|
+
)
|
1272
|
+
return this
|
1273
|
+
},
|
1274
|
+
|
1275
|
+
// Implementation of character-level selector
|
1276
|
+
// (字元級選擇器)
|
1277
|
+
charify: function( option ) {
|
1278
|
+
var option = $.extend({
|
1279
|
+
hanzi: 'individual',
|
1280
|
+
// individual || group || biaodian || none
|
1281
|
+
liga: 'liga',
|
1282
|
+
// liga || none
|
1283
|
+
word: 'group',
|
1284
|
+
// group || punctuation || none
|
1285
|
+
|
1286
|
+
latin: 'group',
|
1287
|
+
ellinika: 'group',
|
1288
|
+
kirillica: 'group',
|
1289
|
+
kana: 'none',
|
1290
|
+
eonmun: 'none'
|
1291
|
+
// group || individual || none
|
1292
|
+
}, option || {})
|
1293
|
+
|
1294
|
+
// CJK and biaodian
|
1295
|
+
if ( option.hanzi === 'group' ) {
|
1296
|
+
this.wrap( TYPESET.char.hanzi.group, $.clone( $.create( 'char_group', 'hanzi cjk' )))
|
1297
|
+
}
|
1298
|
+
if ( option.hanzi === 'individual' ) {
|
1299
|
+
this.wrap( TYPESET.char.hanzi.individual, $.clone( $.create( 'char', 'hanzi cjk' )))
|
1300
|
+
}
|
1301
|
+
|
1302
|
+
if ( option.hanzi === 'individual' ||
|
1303
|
+
option.hanzi === 'biaodian' ||
|
1304
|
+
option.liga === 'liga'
|
1305
|
+
) {
|
1306
|
+
if ( option.hanzi !== 'none' ) {
|
1307
|
+
this.replace(
|
1308
|
+
TYPESET.char.biaodian.all,
|
1309
|
+
function( portion, match ) {
|
1310
|
+
var mat = match[0]
|
1311
|
+
var text = $.create( '', mat )
|
1312
|
+
var clazz = 'biaodian cjk ' + (
|
1313
|
+
mat.match( TYPESET.char.biaodian.open ) ? 'open' :
|
1314
|
+
mat.match( TYPESET.char.biaodian.close ) ? 'close end' :
|
1315
|
+
mat.match( TYPESET.char.biaodian.end ) ? 'end' : ''
|
1316
|
+
)
|
1317
|
+
var elem = $.create( 'char', clazz )
|
1318
|
+
var unicode = mat.charCodeAt( 0 ).toString( 16 )
|
1319
|
+
|
1320
|
+
elem.setAttribute( 'unicode', unicode )
|
1321
|
+
elem.appendChild( text )
|
1322
|
+
return elem
|
1323
|
+
}
|
1324
|
+
)
|
1325
|
+
}
|
1326
|
+
|
1327
|
+
this.replace(
|
1328
|
+
option.liga === 'liga' ? TYPESET.char.biaodian.liga :
|
1329
|
+
new RegExp( '(' + UNICODE.biaodian.liga + ')', 'g' ),
|
1330
|
+
function( portion, match ) {
|
1331
|
+
var mat = match[0]
|
1332
|
+
var text = $.create( '', mat )
|
1333
|
+
var elem = $.create( 'char', 'biaodian liga cjk' )
|
1334
|
+
var unicode = mat.charCodeAt( 0 ).toString( 16 )
|
1335
|
+
|
1336
|
+
elem.setAttribute( 'unicode', unicode )
|
1337
|
+
elem.appendChild( text )
|
1338
|
+
return elem
|
1339
|
+
}
|
1340
|
+
)
|
1341
|
+
}
|
1342
|
+
|
1343
|
+
// Western languages (word-level)
|
1344
|
+
if ( option.word !== 'none' ) {
|
1345
|
+
this.wrap( TYPESET.char.word, $.clone( $.create( 'word' )))
|
1346
|
+
}
|
1347
|
+
|
1348
|
+
// Western languages (alphabet-level)
|
1349
|
+
if ( option.latin !== 'none' ||
|
1350
|
+
option.ellinika !== 'none' ||
|
1351
|
+
option.kirillica !== 'none'
|
1352
|
+
) {
|
1353
|
+
this.wrap( TYPESET.char.punct.all, $.clone( $.create( 'char', 'punct' )))
|
1354
|
+
}
|
1355
|
+
if ( option.latin === 'individual' ) {
|
1356
|
+
this.wrap( TYPESET.char.alphabet.latin, $.clone( $.create( 'char', 'alphabet latin' )))
|
1357
|
+
}
|
1358
|
+
if ( option.ellinika === 'individual' ) {
|
1359
|
+
this.wrap( TYPESET.char.alphabet.ellinika, $.clone( $.create( 'char', 'alphabet ellinika greek' )))
|
1360
|
+
}
|
1361
|
+
if ( option.kirillica === 'individual' ) {
|
1362
|
+
this.wrap( TYPESET.char.alphabet.kirillica, $.clone( $.create( 'char', 'alphabet kirillica cyrillic' )))
|
1363
|
+
}
|
1364
|
+
return this
|
1365
|
+
}
|
1366
|
+
})
|
1367
|
+
|
1368
|
+
Han.find = Fibre
|
1369
|
+
|
1370
|
+
void [
|
1371
|
+
'replace',
|
1372
|
+
'wrap',
|
1373
|
+
'revert',
|
1374
|
+
'jinzify',
|
1375
|
+
'charify'
|
1376
|
+
].forEach(function( method ) {
|
1377
|
+
Han.fn[ method ] = function() {
|
1378
|
+
if ( !this.finder ) {
|
1379
|
+
// Share the same selector
|
1380
|
+
this.finder = Han.find( this.context )
|
1381
|
+
}
|
1382
|
+
|
1383
|
+
this.finder[ method ]( arguments[ 0 ], arguments[ 1 ] )
|
1384
|
+
return this
|
1385
|
+
}
|
1386
|
+
})
|
1387
|
+
|
1388
|
+
var Hyu = {
|
1389
|
+
JS_RENDERED_CLASS: 'hyu-js-rendered'
|
1390
|
+
}
|
1391
|
+
|
1392
|
+
function writeOnCanvas( text, font ) {
|
1393
|
+
var canvas = $.create( 'canvas' )
|
1394
|
+
var context
|
1395
|
+
|
1396
|
+
canvas.width = '50'
|
1397
|
+
canvas.height = '20'
|
1398
|
+
canvas.style.display = 'none'
|
1399
|
+
|
1400
|
+
body.appendChild( canvas )
|
1401
|
+
|
1402
|
+
context = canvas.getContext( '2d' )
|
1403
|
+
context.textBaseline = 'top'
|
1404
|
+
context.font = '15px ' + font + ', sans-serif'
|
1405
|
+
context.fillStyle = 'black'
|
1406
|
+
context.strokeStyle = 'black'
|
1407
|
+
context.fillText( text, 0, 0 )
|
1408
|
+
|
1409
|
+
return [ canvas, context ]
|
1410
|
+
}
|
1411
|
+
|
1412
|
+
function detectFont( treat, control, text ) {
|
1413
|
+
var treat = treat
|
1414
|
+
var control = control
|
1415
|
+
var text = text || '辭Q'
|
1416
|
+
var ret
|
1417
|
+
|
1418
|
+
try {
|
1419
|
+
control = writeOnCanvas( text, control || 'sans-serif' )
|
1420
|
+
treat = writeOnCanvas( text, treat )
|
1421
|
+
|
1422
|
+
for ( var j = 1; j <= 20; j++ ) {
|
1423
|
+
for ( var i = 1; i <= 50; i++ ) {
|
1424
|
+
if (
|
1425
|
+
ret !== 'undefined' &&
|
1426
|
+
treat[1].getImageData(i, j, 1, 1).data[3] !== control[1].getImageData(i, j, 1, 1).data[3]
|
1427
|
+
) {
|
1428
|
+
ret = true
|
1429
|
+
break
|
1430
|
+
} else if ( ret ) {
|
1431
|
+
break
|
1432
|
+
}
|
1433
|
+
|
1434
|
+
if ( i === 50 && j === 20 && !ret ) {
|
1435
|
+
ret = false
|
1436
|
+
}
|
1437
|
+
}
|
1438
|
+
}
|
1439
|
+
|
1440
|
+
// Remove and clean from memory
|
1441
|
+
$.remove( control[0] )
|
1442
|
+
$.remove( treat[0] )
|
1443
|
+
control = null
|
1444
|
+
treat = null
|
1445
|
+
|
1446
|
+
return ret
|
1447
|
+
} catch ( e ) {
|
1448
|
+
return false
|
1449
|
+
}
|
1450
|
+
}
|
1451
|
+
|
1452
|
+
Hyu.detectFont = detectFont
|
1453
|
+
|
1454
|
+
Hyu.support = (function() {
|
1455
|
+
|
1456
|
+
var PREFIX = 'Webkit Moz ms'.split(' ')
|
1457
|
+
|
1458
|
+
// Create an element for feature detecting
|
1459
|
+
// (in `testCSSProp`)
|
1460
|
+
var elem = $.create( '_' )
|
1461
|
+
var exposed = {}
|
1462
|
+
|
1463
|
+
function testCSSProp( prop ) {
|
1464
|
+
var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1)
|
1465
|
+
var allProp = ( prop + ' ' + PREFIX.join( ucProp + ' ' ) + ucProp ).split(' ')
|
1466
|
+
var ret
|
1467
|
+
|
1468
|
+
allProp.forEach(function( prop ) {
|
1469
|
+
if ( typeof elem.style[ prop ] === 'string' ) {
|
1470
|
+
ret = true
|
1471
|
+
}
|
1472
|
+
})
|
1473
|
+
return ret || false
|
1474
|
+
}
|
1475
|
+
|
1476
|
+
function injectElementWithStyle( rule, callback ) {
|
1477
|
+
var fakeBody = body || $.create( 'body' )
|
1478
|
+
var div = $.create( 'div' )
|
1479
|
+
var container = body ? div : fakeBody
|
1480
|
+
var callback = typeof callback === 'function' ? callback : function() {}
|
1481
|
+
var style, ret, docOverflow
|
1482
|
+
|
1483
|
+
style = [ '<style>', rule, '</style>' ].join('')
|
1484
|
+
|
1485
|
+
container.innerHTML += style
|
1486
|
+
fakeBody.appendChild( div )
|
1487
|
+
|
1488
|
+
if ( !body ) {
|
1489
|
+
fakeBody.style.background = ''
|
1490
|
+
fakeBody.style.overflow = 'hidden'
|
1491
|
+
docOverflow = root.style.overflow
|
1492
|
+
|
1493
|
+
root.style.overflow = 'hidden'
|
1494
|
+
root.appendChild( fakeBody )
|
1495
|
+
}
|
1496
|
+
|
1497
|
+
// Callback
|
1498
|
+
ret = callback( container, rule )
|
1499
|
+
|
1500
|
+
// Remove the injected scope
|
1501
|
+
$.remove( container )
|
1502
|
+
if ( !body ) {
|
1503
|
+
root.style.overflow = docOverflow
|
1504
|
+
}
|
1505
|
+
return !!ret
|
1506
|
+
}
|
1507
|
+
|
1508
|
+
function getStyle( elem, prop ) {
|
1509
|
+
var ret
|
1510
|
+
|
1511
|
+
if ( window.getComputedStyle ) {
|
1512
|
+
ret = document.defaultView.getComputedStyle( elem, null ).getPropertyValue( prop )
|
1513
|
+
} else if ( elem.currentStyle ) {
|
1514
|
+
// for IE
|
1515
|
+
ret = elem.currentStyle[ prop ]
|
1516
|
+
}
|
1517
|
+
return ret
|
1518
|
+
}
|
1519
|
+
|
1520
|
+
return {
|
1521
|
+
ruby: (function() {
|
1522
|
+
var ruby = $.create( 'ruby' )
|
1523
|
+
var rt = $.create( 'rt' )
|
1524
|
+
var rp = $.create( 'rp' )
|
1525
|
+
var ret
|
1526
|
+
|
1527
|
+
ruby.appendChild( rp )
|
1528
|
+
ruby.appendChild( rt )
|
1529
|
+
root.appendChild( ruby )
|
1530
|
+
|
1531
|
+
// Browsers that support ruby hide the `<rp>` via `display: none`
|
1532
|
+
ret = (
|
1533
|
+
getStyle( rp, 'display' ) === 'none' ||
|
1534
|
+
// but in IE, `<rp>` has `display: inline`
|
1535
|
+
// so, the test needs other conditions:
|
1536
|
+
getStyle( ruby, 'display' ) === 'ruby' &&
|
1537
|
+
getStyle( rt, 'display' ) === 'ruby-text'
|
1538
|
+
) ? true : false
|
1539
|
+
|
1540
|
+
// Remove and clean from memory
|
1541
|
+
root.removeChild( ruby )
|
1542
|
+
ruby = null
|
1543
|
+
rt = null
|
1544
|
+
rp = null
|
1545
|
+
|
1546
|
+
return ret
|
1547
|
+
})(),
|
1548
|
+
|
1549
|
+
fontface: (function() {
|
1550
|
+
var ret
|
1551
|
+
|
1552
|
+
injectElementWithStyle(
|
1553
|
+
'@font-face { font-family: font; src: url("//"); }',
|
1554
|
+
function( node, rule ) {
|
1555
|
+
var style = $.qsa( 'style', node )[0]
|
1556
|
+
var sheet = style.sheet || style.styleSheet
|
1557
|
+
var cssText = sheet ?
|
1558
|
+
( sheet.cssRules && sheet.cssRules[0] ?
|
1559
|
+
sheet.cssRules[0].cssText : sheet.cssText || ''
|
1560
|
+
) : ''
|
1561
|
+
|
1562
|
+
ret = /src/i.test( cssText ) &&
|
1563
|
+
cssText.indexOf( rule.split(' ')[0] ) === 0
|
1564
|
+
}
|
1565
|
+
)
|
1566
|
+
|
1567
|
+
return ret
|
1568
|
+
})(),
|
1569
|
+
|
1570
|
+
// Address feature support test for `unicode-range` via
|
1571
|
+
// detecting whether it's Arial (supported) or
|
1572
|
+
// Times New Roman (not supported).
|
1573
|
+
unicoderange: (function() {
|
1574
|
+
var ret
|
1575
|
+
|
1576
|
+
injectElementWithStyle(
|
1577
|
+
'@font-face{font-family:test-for-unicode-range;src:local(Arial),local("Droid Sans")}@font-face{font-family:test-for-unicode-range;src:local("Times New Roman"),local(Times),local("Droid Serif");unicode-range:U+270C}',
|
1578
|
+
function() {
|
1579
|
+
ret = !Hyu.detectFont(
|
1580
|
+
'test-for-unicode-range', // treatment group
|
1581
|
+
'Arial, "Droid Sans"', // control group
|
1582
|
+
'Q' // ASCII characters only
|
1583
|
+
)
|
1584
|
+
}
|
1585
|
+
)
|
1586
|
+
return ret
|
1587
|
+
})(),
|
1588
|
+
|
1589
|
+
columnwidth: testCSSProp( 'columnWidth' ),
|
1590
|
+
|
1591
|
+
textemphasis: testCSSProp( 'textEmphasis' ),
|
1592
|
+
|
1593
|
+
writingmode: testCSSProp( 'writingMode' )
|
1594
|
+
}
|
1595
|
+
})()
|
1596
|
+
|
1597
|
+
Hyu.initCond = function( target ) {
|
1598
|
+
var target = target || root
|
1599
|
+
var ret = ''
|
1600
|
+
var clazz
|
1601
|
+
|
1602
|
+
target.classList.add( Hyu.JS_RENDERED_CLASS )
|
1603
|
+
|
1604
|
+
for ( var feature in Hyu.support ) {
|
1605
|
+
clazz = ( Hyu.support[ feature ] ? '' : 'no-' ) + feature
|
1606
|
+
|
1607
|
+
target.classList.add( clazz )
|
1608
|
+
ret += clazz + ' '
|
1609
|
+
}
|
1610
|
+
return ret
|
1611
|
+
}
|
1612
|
+
|
1613
|
+
/**
|
1614
|
+
* Create and return a new `<ru>` element
|
1615
|
+
* according to the given contents
|
1616
|
+
*/
|
1617
|
+
function createNormalRu( $rb, $rt, attr ) {
|
1618
|
+
var $ru = $.create( 'ru' )
|
1619
|
+
var $rt = $.clone( $rt )
|
1620
|
+
var attr = attr || {}
|
1621
|
+
|
1622
|
+
if ( Array.isArray( $rb )) {
|
1623
|
+
$ru.innerHTML = $rb.map(function( rb ) {
|
1624
|
+
if (typeof rb == 'undefined') { return '' }
|
1625
|
+
return rb.outerHTML
|
1626
|
+
}).join('')
|
1627
|
+
} else {
|
1628
|
+
$ru.appendChild( $.clone( $rb ))
|
1629
|
+
}
|
1630
|
+
|
1631
|
+
$ru.appendChild( $rt )
|
1632
|
+
attr.annotation = $rt.textContent
|
1633
|
+
$.setAttr( $ru, attr )
|
1634
|
+
return $ru
|
1635
|
+
}
|
1636
|
+
|
1637
|
+
/**
|
1638
|
+
* Create and return a new `<ru>` element
|
1639
|
+
* in Zhuyin form
|
1640
|
+
*/
|
1641
|
+
function createZhuyinRu( $rb, $rt ) {
|
1642
|
+
var $rb = $.clone( $rb )
|
1643
|
+
|
1644
|
+
// Create an element to return
|
1645
|
+
var $ru = $.create( 'ru' )
|
1646
|
+
var $zhuyin = $.create( 'zhuyin' )
|
1647
|
+
var $yin = $.create( 'yin' )
|
1648
|
+
var $diao = $.create( 'diao' )
|
1649
|
+
|
1650
|
+
// #### Explanation ####
|
1651
|
+
// * `zhuyin`: the entire phonetic annotation
|
1652
|
+
// * `yin`: the plain pronunciation (w/out tone)
|
1653
|
+
// * `diao`: the tone
|
1654
|
+
// * `form`: the combination of the pronunciation
|
1655
|
+
// * `len`: the text length of `yin`
|
1656
|
+
var zhuyin = $rt.textContent
|
1657
|
+
var yin, diao, form, len
|
1658
|
+
|
1659
|
+
yin = zhuyin.replace( TYPESET.zhuyin.diao, '' )
|
1660
|
+
len = yin ? yin.length : 0
|
1661
|
+
diao = zhuyin
|
1662
|
+
.replace( yin, '' )
|
1663
|
+
.replace( /[\u02C5]/g, '\u02C7' )
|
1664
|
+
.replace( /[\u030D]/g, '\u0358' )
|
1665
|
+
|
1666
|
+
form = zhuyin.replace( TYPESET.zhuyin.form, function( s, j, y ) {
|
1667
|
+
return [
|
1668
|
+
s ? 'S' : null,
|
1669
|
+
j ? 'J' : null,
|
1670
|
+
y ? 'Y' : null
|
1671
|
+
].join('')
|
1672
|
+
})
|
1673
|
+
// - <ru>
|
1674
|
+
// - <rb><rb/>
|
1675
|
+
// - <zhuyin>
|
1676
|
+
// - <yin></yin>
|
1677
|
+
// - <diao></diao>
|
1678
|
+
// - </zhuyin>
|
1679
|
+
// - </ru>
|
1680
|
+
$diao.innerHTML = diao
|
1681
|
+
$yin.innerHTML = yin
|
1682
|
+
$zhuyin.appendChild( $yin )
|
1683
|
+
$zhuyin.appendChild( $diao )
|
1684
|
+
|
1685
|
+
$ru.appendChild( $rb )
|
1686
|
+
$ru.appendChild( $zhuyin )
|
1687
|
+
|
1688
|
+
// Finally, set up the necessary attribute
|
1689
|
+
// and return the new `<ru>`
|
1690
|
+
$.setAttr( $ru, {
|
1691
|
+
zhuyin: '',
|
1692
|
+
diao: diao,
|
1693
|
+
length: len,
|
1694
|
+
form: form
|
1695
|
+
})
|
1696
|
+
return $ru
|
1697
|
+
}
|
1698
|
+
|
1699
|
+
/**
|
1700
|
+
* Normalisation rendering mechanism
|
1701
|
+
*/
|
1702
|
+
$.extend( Hyu, {
|
1703
|
+
|
1704
|
+
// Render and normalise the given context by routine:
|
1705
|
+
//
|
1706
|
+
// > ruby > u, ins > s, del > em
|
1707
|
+
//
|
1708
|
+
renderElem: function( context ) {
|
1709
|
+
this.renderRuby( context )
|
1710
|
+
this.renderDecoLine( context )
|
1711
|
+
this.renderDecoLine( context, 's, del' )
|
1712
|
+
this.renderEm( context )
|
1713
|
+
},
|
1714
|
+
|
1715
|
+
// Traverse target elements (those with text-decoration
|
1716
|
+
// -line) to see if we should address spacing in
|
1717
|
+
// between for semantic presentation.
|
1718
|
+
renderDecoLine: function( context, target ) {
|
1719
|
+
var target = target || 'u, ins'
|
1720
|
+
var $target = $.qsa( target, context )
|
1721
|
+
var rTarget = new RegExp( '^(' + target.replace(/\,\s?/g, '|') + ')$', 'ig' )
|
1722
|
+
|
1723
|
+
$target
|
1724
|
+
.forEach(function( elem ) {
|
1725
|
+
var next
|
1726
|
+
|
1727
|
+
// Ignore all `<wbr>` and comments in between
|
1728
|
+
do {
|
1729
|
+
next = ( next || elem ).nextSibling
|
1730
|
+
if ( !next ) return
|
1731
|
+
} while ( $.isIgnorable( next ))
|
1732
|
+
|
1733
|
+
if ( next.nodeName.match( rTarget )) {
|
1734
|
+
next.classList.add( 'adjacent' )
|
1735
|
+
}
|
1736
|
+
})
|
1737
|
+
},
|
1738
|
+
|
1739
|
+
// Traverse target elements to render Hanzi emphasis marks
|
1740
|
+
// and skip that in punctuation
|
1741
|
+
renderEm: function( context, target ) {
|
1742
|
+
var method = target ? 'qsa' : 'tag'
|
1743
|
+
var target = target || 'em'
|
1744
|
+
var $target = $[ method ]( target, context )
|
1745
|
+
|
1746
|
+
$target
|
1747
|
+
.forEach(function( elem ) {
|
1748
|
+
var $elem = Fibre( elem )
|
1749
|
+
|
1750
|
+
if ( !Hyu.support.textemphasis ) {
|
1751
|
+
$elem.jinzify()
|
1752
|
+
}
|
1753
|
+
|
1754
|
+
$elem
|
1755
|
+
.groupify()
|
1756
|
+
.charify( Hyu.support.textemphasis ? {
|
1757
|
+
hanzi: 'biaodian',
|
1758
|
+
word: 'punctuation'
|
1759
|
+
} : {
|
1760
|
+
latin: 'individual',
|
1761
|
+
ellinika: 'individual',
|
1762
|
+
kirillica: 'individual'
|
1763
|
+
})
|
1764
|
+
})
|
1765
|
+
},
|
1766
|
+
|
1767
|
+
// Address normalisation for both simple and complex
|
1768
|
+
// rubies
|
1769
|
+
renderRuby: function( context, target ) {
|
1770
|
+
var method = target ? 'qsa' : 'tag'
|
1771
|
+
var target = target || 'ruby'
|
1772
|
+
var $target = $[ method ]( target, context )
|
1773
|
+
var $simpClaElem = $.qsa( target + ', rtc', context )
|
1774
|
+
|
1775
|
+
// First of all, simplify semantic classes
|
1776
|
+
$simpClaElem
|
1777
|
+
.forEach(function( elem ) {
|
1778
|
+
var clazz = elem.classList
|
1779
|
+
|
1780
|
+
if ( clazz.contains( 'pinyin' )) {
|
1781
|
+
clazz.add( 'romanization' )
|
1782
|
+
} else if ( clazz.contains( 'mps' )) {
|
1783
|
+
clazz.add( 'zhuyin' )
|
1784
|
+
}
|
1785
|
+
|
1786
|
+
if ( clazz.contains( 'romanization' )) {
|
1787
|
+
clazz.add( 'annotation' )
|
1788
|
+
}
|
1789
|
+
})
|
1790
|
+
|
1791
|
+
// Deal with `<ruby>`
|
1792
|
+
$target
|
1793
|
+
.forEach(function( ruby ) {
|
1794
|
+
var clazz = ruby.classList
|
1795
|
+
|
1796
|
+
var condition = (
|
1797
|
+
!Hyu.support.ruby ||
|
1798
|
+
clazz.contains( 'zhuyin') ||
|
1799
|
+
clazz.contains( 'complex' ) ||
|
1800
|
+
clazz.contains( 'rightangle' )
|
1801
|
+
)
|
1802
|
+
|
1803
|
+
var frag, $cloned, $rb, $ru, maxspan, hruby
|
1804
|
+
|
1805
|
+
if ( !condition ) return
|
1806
|
+
|
1807
|
+
// Apply document fragment here to avoid
|
1808
|
+
// continuously pointless re-paint
|
1809
|
+
frag = $.create( '!' )
|
1810
|
+
frag.appendChild( $.clone( ruby ))
|
1811
|
+
$cloned = $.qsa( target, frag )[0]
|
1812
|
+
|
1813
|
+
// 1. Simple ruby polyfill for, um, Firefox;
|
1814
|
+
// 2. Zhuyin polyfill for all.
|
1815
|
+
if ( !Hyu.support.ruby || clazz.contains( 'zhuyin' )) {
|
1816
|
+
|
1817
|
+
$
|
1818
|
+
.tag( 'rt', $cloned )
|
1819
|
+
.forEach(function( rt ) {
|
1820
|
+
var $rb = $.create( '!' )
|
1821
|
+
var airb = []
|
1822
|
+
var irb
|
1823
|
+
|
1824
|
+
// Consider the previous nodes the implied
|
1825
|
+
// ruby base
|
1826
|
+
do {
|
1827
|
+
irb = ( irb || rt ).previousSibling
|
1828
|
+
|
1829
|
+
if ( !irb || irb.nodeName.match( /(r[ubt])/i )) break
|
1830
|
+
|
1831
|
+
$rb.insertBefore( $.clone( irb ), $rb.firstChild )
|
1832
|
+
airb.push( irb )
|
1833
|
+
} while ( !irb.nodeName.match( /(r[ubt])/i ))
|
1834
|
+
// Create a real `<ru>` to append.
|
1835
|
+
$ru = clazz.contains( 'zhuyin' ) ?
|
1836
|
+
createZhuyinRu( $rb, rt ) : createNormalRu( $rb, rt )
|
1837
|
+
|
1838
|
+
// Replace the ruby text with the new `<ru>`,
|
1839
|
+
// and remove the original implied ruby base(s)
|
1840
|
+
try {
|
1841
|
+
rt.parentNode.replaceChild( $ru, rt )
|
1842
|
+
|
1843
|
+
airb
|
1844
|
+
.forEach(function( irb ) {
|
1845
|
+
$.remove( irb )
|
1846
|
+
})
|
1847
|
+
} catch ( e ) {}
|
1848
|
+
})
|
1849
|
+
}
|
1850
|
+
|
1851
|
+
// 3. Complex ruby polyfill
|
1852
|
+
// - Double-lined annotation;
|
1853
|
+
// - Right-angled annotation.
|
1854
|
+
if ( clazz.contains( 'complex' ) || clazz.contains( 'rightangle' )) {
|
1855
|
+
$rb = $ru = $.tag( 'rb', $cloned )
|
1856
|
+
maxspan = $rb.length
|
1857
|
+
|
1858
|
+
// First of all, deal with Zhuyin containers
|
1859
|
+
// individually
|
1860
|
+
//
|
1861
|
+
// Note that we only support one single Zhuyin
|
1862
|
+
// container in each complex ruby
|
1863
|
+
!function( rtc ) {
|
1864
|
+
if ( !rtc ) return
|
1865
|
+
|
1866
|
+
$ru = $
|
1867
|
+
.tag( 'rt', rtc )
|
1868
|
+
.map(function( rt, i ) {
|
1869
|
+
if ( !$rb[ i ] ) return
|
1870
|
+
var ret = createZhuyinRu( $rb[ i ], rt )
|
1871
|
+
|
1872
|
+
try {
|
1873
|
+
$rb[ i ].parentNode.replaceChild( ret, $rb[ i ] )
|
1874
|
+
} catch ( e ) {}
|
1875
|
+
return ret
|
1876
|
+
})
|
1877
|
+
|
1878
|
+
// Remove the container once it's useless
|
1879
|
+
$.remove( rtc )
|
1880
|
+
ruby.setAttribute( 'rightangle', '' )
|
1881
|
+
}( $cloned.querySelector( 'rtc.zhuyin' ))
|
1882
|
+
|
1883
|
+
// Then, normal annotations other than Zhuyin
|
1884
|
+
$
|
1885
|
+
.qsa( 'rtc:not(.zhuyin)', $cloned )
|
1886
|
+
.forEach(function( rtc, order ) {
|
1887
|
+
var ret
|
1888
|
+
ret = $
|
1889
|
+
.tag( 'rt', rtc )
|
1890
|
+
.map(function( rt, i ) {
|
1891
|
+
var rbspan = Number( rt.getAttribute( 'rbspan' ) || 1 )
|
1892
|
+
var span = 0
|
1893
|
+
var aRb = []
|
1894
|
+
var rb, ret
|
1895
|
+
|
1896
|
+
if ( rbspan > maxspan ) {
|
1897
|
+
rbspan = maxspan
|
1898
|
+
}
|
1899
|
+
|
1900
|
+
do {
|
1901
|
+
try {
|
1902
|
+
rb = $ru.shift()
|
1903
|
+
aRb.push( rb )
|
1904
|
+
} catch (e) {}
|
1905
|
+
if (typeof rb == 'undefined') { break }
|
1906
|
+
span += Number( rb.getAttribute( 'span' ) || 1 )
|
1907
|
+
} while ( rbspan > span )
|
1908
|
+
|
1909
|
+
if ( rbspan < span ) {
|
1910
|
+
if ( aRb.length > 1 ) {
|
1911
|
+
console.error( 'An impossible `rbspan` value detected.', ruby )
|
1912
|
+
return
|
1913
|
+
}
|
1914
|
+
aRb = $.tag( 'rb', aRb[0] )
|
1915
|
+
$ru = aRb.slice( rbspan ).concat( $ru )
|
1916
|
+
aRb = aRb.slice( 0, rbspan )
|
1917
|
+
span = rbspan
|
1918
|
+
}
|
1919
|
+
|
1920
|
+
ret = createNormalRu( aRb, rt, {
|
1921
|
+
'class': clazz,
|
1922
|
+
span: span,
|
1923
|
+
order: order
|
1924
|
+
})
|
1925
|
+
|
1926
|
+
try {
|
1927
|
+
aRb[0].parentNode.replaceChild( ret, aRb.shift())
|
1928
|
+
aRb.forEach(function( rb ) {
|
1929
|
+
$.remove( rb )
|
1930
|
+
})
|
1931
|
+
} catch (e) {}
|
1932
|
+
|
1933
|
+
return ret
|
1934
|
+
})
|
1935
|
+
$ru = ret
|
1936
|
+
// Remove the container once it's useless
|
1937
|
+
$.remove( rtc )
|
1938
|
+
})
|
1939
|
+
}
|
1940
|
+
// Create a new fake `<hruby>` element so the
|
1941
|
+
// style sheets will render it as a polyfill,
|
1942
|
+
// which also helps to avoid the UA style.
|
1943
|
+
//
|
1944
|
+
// (The ‘H’ stands for ‘Han’, by the way)
|
1945
|
+
hruby = $.create( 'hruby' )
|
1946
|
+
hruby.innerHTML = frag.firstChild.innerHTML
|
1947
|
+
|
1948
|
+
// Copy all attributes onto it
|
1949
|
+
$.setAttr( hruby, ruby.attributes )
|
1950
|
+
hruby.normalize()
|
1951
|
+
|
1952
|
+
// Finally, replace it
|
1953
|
+
ruby.parentNode.replaceChild( hruby, ruby )
|
1954
|
+
})
|
1955
|
+
}
|
1956
|
+
|
1957
|
+
// ### TODO list ###
|
1958
|
+
//
|
1959
|
+
// * Debug mode
|
1960
|
+
// * Better error-tolerance
|
1961
|
+
})
|
1962
|
+
|
1963
|
+
/*!
|
1964
|
+
* Hyu
|
1965
|
+
* css.hanzi.co/hyu
|
1966
|
+
*
|
1967
|
+
* This module is a subset project of Han,
|
1968
|
+
* which aims to provide HTML5-ready and
|
1969
|
+
* Hanzi-optimised style normalisation.
|
1970
|
+
*/
|
1971
|
+
|
1972
|
+
Han.normalize = Hyu
|
1973
|
+
Han.support = Hyu.support
|
1974
|
+
Han.detectFont = Hyu.detectFont
|
1975
|
+
|
1976
|
+
Han.fn.initCond = function() {
|
1977
|
+
this.condition.classList.add( 'han-js-rendered' )
|
1978
|
+
Han.normalize.initCond( this.condition )
|
1979
|
+
return this
|
1980
|
+
}
|
1981
|
+
|
1982
|
+
void [
|
1983
|
+
'Elem',
|
1984
|
+
'DecoLine',
|
1985
|
+
'Em',
|
1986
|
+
'Ruby'
|
1987
|
+
].forEach(function( elem ) {
|
1988
|
+
var method = 'render' + elem
|
1989
|
+
|
1990
|
+
Han.fn[ method ] = function( target ) {
|
1991
|
+
Han.normalize[ method ]( this.context, target )
|
1992
|
+
return this
|
1993
|
+
}
|
1994
|
+
})
|
1995
|
+
|
1996
|
+
$.extend( Han.support, {
|
1997
|
+
// Assume that all devices support Heiti for we
|
1998
|
+
// use `sans-serif` to do the comparison.
|
1999
|
+
heiti: true,
|
2000
|
+
// 'heiti-gb': true,
|
2001
|
+
|
2002
|
+
songti: Han.detectFont( '"Han Songti"' ),
|
2003
|
+
'songti-gb': Han.detectFont( '"Han Songti GB"' ),
|
2004
|
+
|
2005
|
+
kaiti: Han.detectFont( '"Han Kaiti"' ),
|
2006
|
+
// 'kaiti-gb': Han.detectFont( '"Han Kaiti GB"' ),
|
2007
|
+
|
2008
|
+
fangsong: Han.detectFont( '"Han Fangsong"' )
|
2009
|
+
// 'fangsong-gb': Han.detectFont( '"Han Fangsong GB"' )
|
2010
|
+
})
|
2011
|
+
|
2012
|
+
var QUERY_HWS_AS_FIRST_CHILD = '* > hws:first-child, * > wbr:first-child + hws, wbr:first-child + wbr + hws'
|
2013
|
+
|
2014
|
+
//// Disabled `Node.normalize()` for temp due to
|
2015
|
+
//// issue below in IE11.
|
2016
|
+
//// See: http://stackoverflow.com/questions/22337498/why-does-ie11-handle-node-normalize-incorrectly-for-the-minus-symbol
|
2017
|
+
var isNodeNormalizeNormal = (function() {
|
2018
|
+
var div = $.create( 'div' )
|
2019
|
+
|
2020
|
+
div.appendChild( $.create( '', '0-' ))
|
2021
|
+
div.appendChild( $.create( '', '2' ))
|
2022
|
+
div.normalize()
|
2023
|
+
|
2024
|
+
return div.firstChild.length !== 2
|
2025
|
+
})(),
|
2026
|
+
|
2027
|
+
hws
|
2028
|
+
|
2029
|
+
hws = $.create( 'hws' )
|
2030
|
+
hws.innerHTML = ' '
|
2031
|
+
|
2032
|
+
$.extend( Han, {
|
2033
|
+
isNodeNormalizeNormal: isNodeNormalizeNormal,
|
2034
|
+
|
2035
|
+
renderHWS: function( context, strict ) {
|
2036
|
+
var context = context || document
|
2037
|
+
var mode = strict ? 'strict' : 'base'
|
2038
|
+
var finder = Han.find( context )
|
2039
|
+
|
2040
|
+
// Elements to be filtered according to the
|
2041
|
+
// HWS rendering mode
|
2042
|
+
if ( strict ) {
|
2043
|
+
finder.filterOut( 'textarea, code, kbd, samp, pre', true )
|
2044
|
+
} else {
|
2045
|
+
finder.filterOut( 'textarea', true )
|
2046
|
+
}
|
2047
|
+
|
2048
|
+
finder
|
2049
|
+
.replace( Han.TYPESET.hws[ mode ][0], '$1<hws/>$2' )
|
2050
|
+
.replace( Han.TYPESET.hws[ mode ][1], '$1<hws/>$2' )
|
2051
|
+
|
2052
|
+
// Deal with `' 字'`, `" 字"` => `'字'`, `"字"`
|
2053
|
+
.replace( /(['"]+)<hws\/>(.+?)<hws\/>\1/ig, '$1$2$1' )
|
2054
|
+
|
2055
|
+
// Convert text nodes `<hws/>` into real element nodes
|
2056
|
+
.replace( '<hws/>', function() {
|
2057
|
+
return $.clone( hws )
|
2058
|
+
})
|
2059
|
+
|
2060
|
+
// Deal with:
|
2061
|
+
// `漢<u><hws/>zi</u>` => `漢<hws/><u>zi</u>`
|
2062
|
+
$
|
2063
|
+
.qsa( QUERY_HWS_AS_FIRST_CHILD, context )
|
2064
|
+
.forEach(function( firstChild ) {
|
2065
|
+
var parent = firstChild.parentNode
|
2066
|
+
var target = parent.firstChild
|
2067
|
+
|
2068
|
+
// Skip all `<wbr>` and comments
|
2069
|
+
while ( $.isIgnorable( target )) {
|
2070
|
+
target = target.nextSibling
|
2071
|
+
|
2072
|
+
if ( !target ) return
|
2073
|
+
}
|
2074
|
+
|
2075
|
+
// The ‘first-child’ of DOM is different from
|
2076
|
+
// the ones of QSA, could be either an element
|
2077
|
+
// or a text fragment, but the latter one is
|
2078
|
+
// not what we want. We don't want comments,
|
2079
|
+
// either.
|
2080
|
+
while ( target.nodeName === 'HWS' ) {
|
2081
|
+
$.remove( target, parent )
|
2082
|
+
|
2083
|
+
target = parent.parentNode.insertBefore( $.clone( hws ), parent )
|
2084
|
+
parent = parent.parentNode
|
2085
|
+
|
2086
|
+
if ( isNodeNormalizeNormal ) {
|
2087
|
+
parent.normalize()
|
2088
|
+
}
|
2089
|
+
|
2090
|
+
// This is for extreme circumstances, i.e.,
|
2091
|
+
// `漢<a><b><c><hws/>zi</c></b></a>` =>
|
2092
|
+
// `漢<hws/><a><b><c>zi</c></b></a>`
|
2093
|
+
if ( target !== parent.firstChild ) {
|
2094
|
+
break
|
2095
|
+
}
|
2096
|
+
}
|
2097
|
+
})
|
2098
|
+
|
2099
|
+
// Normalise nodes we messed up with
|
2100
|
+
if ( isNodeNormalizeNormal ) {
|
2101
|
+
context.normalize()
|
2102
|
+
}
|
2103
|
+
|
2104
|
+
// Return the finder instance for future usage
|
2105
|
+
return finder
|
2106
|
+
}
|
2107
|
+
})
|
2108
|
+
|
2109
|
+
$.extend( Han.fn, {
|
2110
|
+
HWS: null,
|
2111
|
+
|
2112
|
+
renderHWS: function( strict ) {
|
2113
|
+
Han.renderHWS( this.context, strict )
|
2114
|
+
|
2115
|
+
this.HWS = $.tag( 'hws', this.context )
|
2116
|
+
return this
|
2117
|
+
},
|
2118
|
+
|
2119
|
+
revertHWS: function() {
|
2120
|
+
this.HWS.forEach(function( hws ) {
|
2121
|
+
$.remove( hws )
|
2122
|
+
})
|
2123
|
+
return this
|
2124
|
+
}
|
2125
|
+
})
|
2126
|
+
|
2127
|
+
Han.renderJiya = function( context ) {
|
2128
|
+
var context = context || document
|
2129
|
+
var finder = [ Han.find( context ) ]
|
2130
|
+
|
2131
|
+
finder[ 0 ].filterOut( 'textarea, code, kbd, samp, pre, jinze, em', true )
|
2132
|
+
finder[ 0 ].groupify()
|
2133
|
+
|
2134
|
+
$
|
2135
|
+
.qsa( 'char_group.biaodian', context )
|
2136
|
+
.forEach(function( elem ) {
|
2137
|
+
finder.push(
|
2138
|
+
Han( elem )
|
2139
|
+
.charify({
|
2140
|
+
hanzi: 'biaodian',
|
2141
|
+
liga: 'liga',
|
2142
|
+
word: 'none',
|
2143
|
+
latin: 'none',
|
2144
|
+
ellinika: 'none',
|
2145
|
+
kirillica: 'none'
|
2146
|
+
})
|
2147
|
+
)
|
2148
|
+
})
|
2149
|
+
return finder
|
2150
|
+
}
|
2151
|
+
|
2152
|
+
$.extend( Han.fn, {
|
2153
|
+
jiya: null,
|
2154
|
+
|
2155
|
+
renderJiya: function() {
|
2156
|
+
this.jiya = Han.renderJiya( this.context )
|
2157
|
+
return this
|
2158
|
+
},
|
2159
|
+
|
2160
|
+
revertJiya: function() {
|
2161
|
+
try {
|
2162
|
+
this.jiya.revert( 'all' )
|
2163
|
+
} catch ( e ) {}
|
2164
|
+
return this
|
2165
|
+
}
|
2166
|
+
})
|
2167
|
+
|
2168
|
+
var mdot
|
2169
|
+
|
2170
|
+
mdot = $.create( 'char', 'biaodian cjk middle' )
|
2171
|
+
mdot.setAttribute( 'unicode', 'b7' )
|
2172
|
+
|
2173
|
+
Han.correctBasicBD = function( context, all ) {
|
2174
|
+
if ( Han.support.unicoderange && !all ) return
|
2175
|
+
|
2176
|
+
var context = context || document
|
2177
|
+
var finder
|
2178
|
+
|
2179
|
+
finder = Han.find( context )
|
2180
|
+
|
2181
|
+
finder
|
2182
|
+
.wrap( /\u00B7/g, $.clone( mdot ))
|
2183
|
+
.charify({
|
2184
|
+
liga: 'liga',
|
2185
|
+
hanzi: 'none',
|
2186
|
+
word: 'none',
|
2187
|
+
latin: 'none',
|
2188
|
+
ellinika: 'none',
|
2189
|
+
kirillica: 'none'
|
2190
|
+
})
|
2191
|
+
}
|
2192
|
+
|
2193
|
+
$.extend( Han.fn, {
|
2194
|
+
basicBD: null,
|
2195
|
+
|
2196
|
+
correctBasicBD: function( all ) {
|
2197
|
+
this.basicBD = Han.correctBasicBD( this.context, all )
|
2198
|
+
return this
|
2199
|
+
},
|
2200
|
+
|
2201
|
+
revertBasicBD: function() {
|
2202
|
+
try {
|
2203
|
+
this.basicBD.revert( 'all' )
|
2204
|
+
} catch (e) {}
|
2205
|
+
return this
|
2206
|
+
}
|
2207
|
+
})
|
2208
|
+
|
2209
|
+
var QUERY_RU_W_ANNO = 'ru[annotation]'
|
2210
|
+
var SELECTOR_TO_IGNORE = 'textarea, code, kbd, samp, pre'
|
2211
|
+
|
2212
|
+
var isCombLigaNormal = (function() {
|
2213
|
+
var fakeBody = body || $.create( 'body' )
|
2214
|
+
var div = $.create( 'div' )
|
2215
|
+
var control = $.create( 'span' )
|
2216
|
+
var container = body ? div : fakeBody
|
2217
|
+
var treat, docOverflow, ret
|
2218
|
+
|
2219
|
+
if ( !body ) {
|
2220
|
+
fakeBody.style.background = ''
|
2221
|
+
fakeBody.style.overflow = 'hidden'
|
2222
|
+
docOverflow = root.style.overflow
|
2223
|
+
|
2224
|
+
root.style.overflow = 'hidden'
|
2225
|
+
root.appendChild( fakeBody )
|
2226
|
+
} else {
|
2227
|
+
body.appendChild( container )
|
2228
|
+
}
|
2229
|
+
|
2230
|
+
control.innerHTML = 'i̍'
|
2231
|
+
control.style.fontFamily = 'sans-serif'
|
2232
|
+
control.style.display = 'inline-block'
|
2233
|
+
|
2234
|
+
treat = $.clone( control )
|
2235
|
+
treat.style.fontFamily = '"Romanization Sans"'
|
2236
|
+
|
2237
|
+
container.appendChild( control )
|
2238
|
+
container.appendChild( treat )
|
2239
|
+
|
2240
|
+
ret = control.clientWidth !== treat.clientWidth
|
2241
|
+
$.remove( container )
|
2242
|
+
|
2243
|
+
if ( !body ) {
|
2244
|
+
root.style.overflow = docOverflow
|
2245
|
+
}
|
2246
|
+
return ret
|
2247
|
+
})()
|
2248
|
+
|
2249
|
+
var aCombLiga = Han.TYPESET[ 'display-as' ][ 'comb-liga-pua' ]
|
2250
|
+
var aInaccurateChar = Han.TYPESET[ 'inaccurate-char' ]
|
2251
|
+
|
2252
|
+
var charCombLiga = $.create( 'char', 'comb-liga' )
|
2253
|
+
var charCombLigaInner = $.create( 'inner' )
|
2254
|
+
|
2255
|
+
$.extend( Han, {
|
2256
|
+
isCombLigaNormal: isCombLigaNormal,
|
2257
|
+
|
2258
|
+
substCombLigaWithPUA: function( context ) {
|
2259
|
+
if ( isCombLigaNormal ) return
|
2260
|
+
|
2261
|
+
var context = context || document
|
2262
|
+
var finder = Han.find( context )
|
2263
|
+
|
2264
|
+
finder.filterOut( SELECTOR_TO_IGNORE, true )
|
2265
|
+
|
2266
|
+
aCombLiga
|
2267
|
+
.forEach(function( pattern ) {
|
2268
|
+
finder
|
2269
|
+
.replace(
|
2270
|
+
new RegExp( pattern[ 0 ], 'ig' ),
|
2271
|
+
function( portion, match ) {
|
2272
|
+
var ret = $.clone( charCombLiga )
|
2273
|
+
var inner = $.clone( charCombLigaInner )
|
2274
|
+
|
2275
|
+
// Put the original content in an inner container
|
2276
|
+
// for better presentational effect of hidden text
|
2277
|
+
inner.innerHTML = match[ 0 ]
|
2278
|
+
ret.appendChild( inner )
|
2279
|
+
ret.setAttribute( 'display-as', pattern[ 1 ] )
|
2280
|
+
return portion.index === 0 ? ret : ''
|
2281
|
+
}
|
2282
|
+
)
|
2283
|
+
})
|
2284
|
+
|
2285
|
+
$
|
2286
|
+
.qsa( QUERY_RU_W_ANNO, context )
|
2287
|
+
.forEach(function( ru ) {
|
2288
|
+
var annotation = ru.getAttribute( 'annotation' )
|
2289
|
+
|
2290
|
+
aCombLiga
|
2291
|
+
// Latin vowels only
|
2292
|
+
.slice( 0, 5 )
|
2293
|
+
.forEach(function( pattern ) {
|
2294
|
+
annotation = annotation.replace(
|
2295
|
+
new RegExp( pattern[ 0 ], 'ig' ), pattern[ 1 ]
|
2296
|
+
)
|
2297
|
+
})
|
2298
|
+
ru.setAttribute( 'annotation', annotation )
|
2299
|
+
})
|
2300
|
+
return finder
|
2301
|
+
},
|
2302
|
+
|
2303
|
+
substInaccurateChar: function( context ) {
|
2304
|
+
var context = context || document
|
2305
|
+
var finder = Han.find( context )
|
2306
|
+
|
2307
|
+
finder.filterOut( SELECTOR_TO_IGNORE, true )
|
2308
|
+
aInaccurateChar
|
2309
|
+
.forEach(function( pattern ) {
|
2310
|
+
finder
|
2311
|
+
.replace(
|
2312
|
+
new RegExp( pattern[ 0 ], 'ig' ),
|
2313
|
+
pattern[ 1 ]
|
2314
|
+
)
|
2315
|
+
})
|
2316
|
+
}
|
2317
|
+
})
|
2318
|
+
|
2319
|
+
$.extend( Han.fn, {
|
2320
|
+
'comb-liga': null,
|
2321
|
+
'inaccurate-char': null,
|
2322
|
+
|
2323
|
+
substCombLigaWithPUA: function() {
|
2324
|
+
this['comb-liga'] = Han.substCombLigaWithPUA( this.context )
|
2325
|
+
return this
|
2326
|
+
},
|
2327
|
+
|
2328
|
+
revertCombLigaWithPUA: function() {
|
2329
|
+
try {
|
2330
|
+
this['comb-liga'].revert( 'all' )
|
2331
|
+
} catch (e) {}
|
2332
|
+
return this
|
2333
|
+
},
|
2334
|
+
|
2335
|
+
substInaccurateChar: function() {
|
2336
|
+
this['inaccurate-char'] = Han.substInaccurateChar( this.context )
|
2337
|
+
return this
|
2338
|
+
},
|
2339
|
+
|
2340
|
+
revertInaccurateChar: function() {
|
2341
|
+
try {
|
2342
|
+
this['inaccurate-char'].revert( 'all' )
|
2343
|
+
} catch (e) {}
|
2344
|
+
return this
|
2345
|
+
}
|
2346
|
+
})
|
2347
|
+
|
2348
|
+
window.addEventListener( 'DOMContentLoaded', function() {
|
2349
|
+
var initContext
|
2350
|
+
|
2351
|
+
// Use the shortcut under the default situation
|
2352
|
+
if ( root.classList.contains( 'han-init' )) {
|
2353
|
+
Han.init()
|
2354
|
+
|
2355
|
+
// Consider ‘a configured context’ the special
|
2356
|
+
// case of the default situation. Will have to
|
2357
|
+
// replace the `Han.init` with the instance as
|
2358
|
+
// well (for future usage).
|
2359
|
+
} else if ( initContext = document.querySelector( '.han-init-context' )) {
|
2360
|
+
Han.init = Han( initContext ).render()
|
2361
|
+
}
|
2362
|
+
})
|
2363
|
+
|
2364
|
+
// AMD
|
2365
|
+
if ( typeof define === 'function' && define.amd ) {
|
2366
|
+
define(function() { return Han })
|
2367
|
+
|
2368
|
+
// Expose to global namespace
|
2369
|
+
} else if ( typeof noGlobalNS === 'undefined' || noGlobalNS === false ) {
|
2370
|
+
window.Han = Han
|
2371
|
+
}
|
2372
|
+
|
2373
|
+
return Han
|
2374
|
+
});
|
2375
|
+
|