hanzi-rails 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * 漢字標準格式 v3.2.0 | MIT License | css.hanzi.co
2
+ * 漢字標準格式 v3.2.7 | MIT License | css.hanzi.co
3
3
  * Han.css: the CSS typography framework optimised for Hanzi
4
4
  */
5
5
 
@@ -8,6 +8,10 @@ void function( global, factory ) {
8
8
  // CommonJS
9
9
  if ( typeof module === 'object' && typeof module.exports === 'object' ) {
10
10
  module.exports = factory( global, true )
11
+ // AMD
12
+ } else if ( typeof define === 'function' && define.amd ) {
13
+ define(function() { return factory( global, true ) })
14
+ // Global namespace
11
15
  } else {
12
16
  factory( global )
13
17
  }
@@ -22,28 +26,34 @@ var root = document.documentElement
22
26
 
23
27
  var body = document.body
24
28
 
25
- var VERSION = '3.2.0'
29
+ var VERSION = '3.2.7'
26
30
 
27
31
  var ROUTINE = [
28
32
  // Initialise the condition with feature-detecting
29
33
  // classes (Modernizr-alike), binding onto the root
30
34
  // element, possibly `<html>`.
31
35
  'initCond',
36
+
32
37
  // Address element normalisation
33
38
  'renderElem',
39
+
34
40
  // Handle Biaodian
35
- //'jinzify',
41
+ /* 'jinzify', */
36
42
  'renderHanging',
37
43
  'renderJiya',
44
+
38
45
  // Address Hanzi and Western script mixed spacing
39
46
  'renderHWS',
47
+
40
48
  // Address Basic Biaodian correction in Firefox
41
49
  'correctBasicBD',
50
+
42
51
  // Address presentational correction to combining ligatures
43
52
  'substCombLigaWithPUA'
53
+
44
54
  // Address semantic correction to inaccurate characters
45
55
  // **Note:** inactivated by default
46
- // 'substInaccurateChar'
56
+ /* 'substInaccurateChar', */
47
57
  ]
48
58
 
49
59
  // Define Han
@@ -289,12 +299,10 @@ var UNICODE = {
289
299
  *
290
300
  * 1. 國語注音、方言音符號:[\u3105-\u312D][\u31A0-\u31BA]
291
301
  Bopomofo phonetic symbols
292
- * 2. 國語陰陽上去聲調號:[\u02D9\u02CA\u02C5\u02C7\u02CB] (**註:**三聲包含乙個不合規範的符號)
293
- Tones for Mandarin
294
- * 3. 方言音陰、陽去聲調號:[\u02EA\u02EB]
295
- Departing tones in dialects
296
- * 4. 方言音陰、陽入韻:[\u31B4-\u31B7][\u0358\u030d]?
297
- Checked tones in dialects
302
+ * 2. 平上去聲調號:[\u02D9\u02CA\u02C5\u02C7\u02EA\u02EB\u02CB] (**註:**國語三聲包含乙個不合規範的符號)
303
+ Level, rising, departing tones
304
+ * 3. 入聲調號:[\u31B4-\u31B7][\u0358\u030d]?
305
+ Checked (entering) tones
298
306
  */
299
307
  zhuyin: {
300
308
  base: '[\u3105-\u312D\u31A0-\u31BA]',
@@ -302,7 +310,7 @@ var UNICODE = {
302
310
  medial: '[\u3127-\u3129]',
303
311
  final: '[\u311A-\u3129\u312D\u31A4-\u31B3\u31B8-\u31BA]',
304
312
  tone: '[\u02D9\u02CA\u02C5\u02C7\u02CB\u02EA\u02EB]',
305
- ruyun: '[\u31B4-\u31B7][\u0358\u030d]?'
313
+ checked: '[\u31B4-\u31B7][\u0358\u030d]?'
306
314
  }
307
315
  }
308
316
 
@@ -349,7 +357,7 @@ var TYPESET = (function() {
349
357
  var rZyS = UNICODE.zhuyin.initial
350
358
  var rZyJ = UNICODE.zhuyin.medial
351
359
  var rZyY = UNICODE.zhuyin.final
352
- var rZyD = UNICODE.zhuyin.tone + '|' + UNICODE.zhuyin.ruyun
360
+ var rZyD = UNICODE.zhuyin.tone + '|' + UNICODE.zhuyin.checked
353
361
 
354
362
  return {
355
363
  /* Character-level selector (字級選擇器)
@@ -384,20 +392,20 @@ var TYPESET = (function() {
384
392
  */
385
393
  group: {
386
394
  biaodian: [
387
- new RegExp( '(' + rBd + '){2,}', 'g' ),
395
+ new RegExp( '((' + rBd + '){2,})', 'g' ),
388
396
  new RegExp( '(' + rBdLiga + rBdOpen + ')', 'g' )
389
397
  ],
390
398
  punct: null,
391
399
  hanzi: new RegExp( '(' + rHan + ')+', 'g' ),
392
400
  western: new RegExp( '(' + rLatn + '|' + rGk + '|' + rCy + '|' + rPt + ')+', 'ig' ),
393
401
  kana: new RegExp( '(' + rKana + '|' + rKanaS + '|' + rKanaH + ')+', 'g' ),
394
- eonmun: new RegExp( '(' + rEon + '|' + rEonH + ')+', 'g' )
402
+ eonmun: new RegExp( '(' + rEon + '|' + rEonH + '|' + rPt + ')+', 'g' )
395
403
  },
396
404
 
397
405
  /* Punctuation Rules (禁則)
398
406
  */
399
407
  jinze: {
400
- hanging: new RegExp( '(' + rWhite + '*)(' + rBdClose + '*|[…⋯]*)([、,。.])(?!' + rBdEnd + ')', 'ig' ),
408
+ hanging: new RegExp( '(' + rBdClose + '*|[…⋯]*)([、,。.])(?!' + rBdEnd + ')', 'ig' ),
401
409
  touwei: new RegExp( '(' + rBdOpen + '+)(' + rChar + ')(' + rBdEnd + '+)', 'ig' ),
402
410
  tou: new RegExp( '(' + rBdOpen + '+)(' + rChar + ')', 'ig' ),
403
411
  wei: new RegExp( '(' + rChar + ')(' + rBdEnd + '+)', 'ig' ),
@@ -415,13 +423,13 @@ var TYPESET = (function() {
415
423
  */
416
424
  hws: {
417
425
  base: [
418
- new RegExp( '('+ rHan +')(' + rAlph + '|' + rPtOpen + ')', 'ig' ),
419
- new RegExp( '('+ rAlph+ '|' + rPtEnd +')(' + rHan + ')', 'ig' )
426
+ new RegExp( '('+ rHan + ')(' + rAlph + '|' + rPtOpen + ')', 'ig' ),
427
+ new RegExp( '('+ rAlph + '|' + rPtEnd + ')(' + rHan + ')', 'ig' )
420
428
  ],
421
429
 
422
430
  strict: [
423
- new RegExp( '('+ rHan +')' + rWhite + '?(' + rAlph + '|' + rPtOpen + ')', 'ig' ),
424
- new RegExp( '('+ rAlph+ '|' + rPtEnd +')' + rWhite + '?(' + rHan + ')', 'ig' )
431
+ new RegExp( '('+ rHan + ')' + rWhite + '?(' + rAlph + '|' + rPtOpen + ')', 'ig' ),
432
+ new RegExp( '('+ rAlph + '|' + rPtEnd + ')' + rWhite + '?(' + rHan + ')', 'ig' )
425
433
  ]
426
434
  },
427
435
 
@@ -446,6 +454,21 @@ var TYPESET = (function() {
446
454
  [ '\u006F[\u030d\u0358]', '\uDB80\uDC6F' ],
447
455
  [ '\u0075[\u030d\u0358]', '\uDB80\uDC75' ],
448
456
 
457
+ [ '\u31B4[\u030d\u0358]', '\uDB8C\uDDB4' ],
458
+ [ '\u31B5[\u030d\u0358]', '\uDB8C\uDDB5' ],
459
+ [ '\u31B6[\u030d\u0358]', '\uDB8C\uDDB6' ],
460
+ [ '\u31B7[\u030d\u0358]', '\uDB8C\uDDB7' ]
461
+ ],
462
+
463
+ 'comb-liga-vowel': [
464
+ [ '\u0061[\u030d\u0358]', '\uDB80\uDC61' ],
465
+ [ '\u0065[\u030d\u0358]', '\uDB80\uDC65' ],
466
+ [ '\u0069[\u030d\u0358]', '\uDB80\uDC69' ],
467
+ [ '\u006F[\u030d\u0358]', '\uDB80\uDC6F' ],
468
+ [ '\u0075[\u030d\u0358]', '\uDB80\uDC75' ]
469
+ ],
470
+
471
+ 'comb-liga-zhuyin': [
449
472
  [ '\u31B4[\u030d\u0358]', '\uDB8C\uDDB4' ],
450
473
  [ '\u31B5[\u030d\u0358]', '\uDB8C\uDDB5' ],
451
474
  [ '\u31B6[\u030d\u0358]', '\uDB8C\uDDB6' ],
@@ -477,6 +500,7 @@ Han.UNICODE.cjk = Han.UNICODE.hanzi
477
500
  Han.UNICODE.greek = Han.UNICODE.ellinika
478
501
  Han.UNICODE.cyrillic = Han.UNICODE.kirillica
479
502
  Han.UNICODE.hangul = Han.UNICODE.eonmun
503
+ Han.UNICODE.zhuyin.ruyun = Han.UNICODE.zhuyin.checked
480
504
 
481
505
  Han.TYPESET.char.cjk = Han.TYPESET.char.hanzi
482
506
  Han.TYPESET.char.greek = Han.TYPESET.char.ellinika
@@ -523,12 +547,12 @@ var $ = {
523
547
  // Clone a node (text, element or fragment) deeply or
524
548
  // childlessly
525
549
  clone: function( node, deep ) {
526
- return node.cloneNode( deep || true )
550
+ return node.cloneNode( typeof deep === 'boolean' ? deep : true )
527
551
  },
528
552
 
529
553
  // Remove a node (text, element or fragment)
530
- remove: function( node, parent ) {
531
- return ( parent || node.parentNode ).removeChild( node )
554
+ remove: function( node ) {
555
+ return node.parentNode.removeChild( node )
532
556
  },
533
557
 
534
558
  // Set attributes all in once with an object
@@ -586,7 +610,7 @@ var $ = {
586
610
 
587
611
  var Fibre =
588
612
  /*!
589
- * Fibre.js v0.1.2 | MIT License | github.com/ethantw/fibre.js
613
+ * Fibre.js v0.2.1 | MIT License | github.com/ethantw/fibre.js
590
614
  * Based on findAndReplaceDOMText
591
615
  */
592
616
 
@@ -594,8 +618,9 @@ function( Finder ) {
594
618
 
595
619
  'use strict'
596
620
 
597
- var VERSION = '0.1.2'
598
- var FILTER_OUT_SELECTOR = 'style, script, head title'
621
+ var VERSION = '0.2.1'
622
+ var NON_INLINE_PROSE = Finder.NON_INLINE_PROSE
623
+ var AVOID_NON_PROSE = Finder.PRESETS.prose.filterElements
599
624
 
600
625
  var global = window || {}
601
626
  var document = global.document || undefined
@@ -614,8 +639,8 @@ function matches( node, selector, bypassNodeType39 ) {
614
639
 
615
640
  if ( typeof document === 'undefined' ) throw new Error( 'Fibre requires a DOM-supported environment.' )
616
641
 
617
- var Fibre = function( context ) {
618
- return new Fibre.fn.init( context )
642
+ var Fibre = function( context, preset ) {
643
+ return new Fibre.fn.init( context, preset )
619
644
  }
620
645
 
621
646
  Fibre.version = VERSION
@@ -626,87 +651,128 @@ Fibre.fn = Fibre.prototype = {
626
651
 
627
652
  version: VERSION,
628
653
 
654
+ finder: [],
655
+
629
656
  context: undefined,
630
657
 
631
- contextSelector: null,
658
+ portionMode: 'retain',
632
659
 
633
- finder: [],
660
+ selector: {},
661
+
662
+ preset: 'prose',
634
663
 
635
- init: function( context ) {
636
- if ( !context ) throw new Error( 'A context is required for Fibre to initialise.' )
664
+ init: function( context, noPreset ) {
665
+ if ( !!noPreset ) this.preset = null
637
666
 
638
- if ( context instanceof Node ) {
639
- this.context = context
667
+ this.selector = {
668
+ context: null,
669
+ filter: [],
670
+ avoid: [],
671
+ boundary: []
672
+ }
673
+
674
+ if ( !context ) {
675
+ throw new Error( 'A context is required for Fibre to initialise.' )
676
+ } else if ( context instanceof Node ) {
677
+ if ( context instanceof Document ) this.context = context.body || context
678
+ else this.context = context
640
679
  } else if ( typeof context === 'string' ) {
641
- this.contextSelector = context
642
680
  this.context = document.querySelector( context )
681
+ this.selector.context = context
643
682
  }
644
-
645
683
  return this
646
684
  },
647
685
 
648
- filterElemFn: function( currentNode ) {
649
- return matches( currentNode, this.filterSelector, true ) &&
650
- !matches( currentNode, this.filterOutSelector )
686
+ filterFn: function( node ) {
687
+ var filter = this.selector.filter.join( ', ' ) || '*'
688
+ var avoid = this.selector.avoid.join( ', ' ) || null
689
+ var result = matches( node, filter, true ) && !matches( node, avoid )
690
+ return ( this.preset === 'prose' ) ? AVOID_NON_PROSE( node ) && result : result
651
691
  },
652
692
 
653
- filterSelector: '*',
693
+ boundaryFn: function( node ) {
694
+ var boundary = this.selector.boundary.join( ', ' ) || null
695
+ var result = matches( node, boundary )
696
+ return ( this.preset === 'prose' ) ? NON_INLINE_PROSE( node ) || result : result
697
+ },
654
698
 
655
699
  filter: function( selector ) {
656
- switch ( typeof selector ) {
657
- case 'string':
658
- this.filterSelector = selector
659
- break
660
- case 'function':
661
- this.filterElemFn = selector
662
- break
663
- default:
664
- return this
700
+ if ( typeof selector === 'string' ) {
701
+ this.selector.filter.push( selector )
665
702
  }
666
703
  return this
667
704
  },
668
705
 
669
- filterOutSelector: FILTER_OUT_SELECTOR,
706
+ endFilter: function( all ) {
707
+ if ( all ) {
708
+ this.selector.filter = []
709
+ } else {
710
+ this.selector.filter.pop()
711
+ }
712
+ return this
713
+ },
670
714
 
671
- filterOut: function( selector, boolExtend ) {
672
- switch( typeof selector ) {
673
- case 'string':
674
- if ( typeof boolExtend !== 'undefined' && boolExtend === true ) {
675
- this.filterOutSelector += ', ' + selector
676
- } else {
677
- this.filterOutSelector = selector
678
- }
679
- break
680
- default:
681
- return this
715
+ avoid: function( selector ) {
716
+ if ( typeof selector === 'string' ) {
717
+ this.selector.avoid.push( selector )
718
+ }
719
+ return this
720
+ },
721
+
722
+ endAvoid: function( all ) {
723
+ if ( all ) {
724
+ this.selector.avoid = []
725
+ } else {
726
+ this.selector.avoid.pop()
682
727
  }
683
728
  return this
684
729
  },
685
730
 
686
- replace: function( regexp, newSubStr, portionMode ) {
731
+ addBoundary: function( selector ) {
732
+ if ( typeof selector === 'string' ) {
733
+ this.selector.boundary.push( selector )
734
+ }
735
+ return this
736
+ },
737
+
738
+ removeBoundary: function() {
739
+ this.selector.boundary = []
740
+ return this
741
+ },
742
+
743
+ setMode: function( portionMode ) {
744
+ this.portionMode = portionMode === 'first' ? 'first' : 'retain'
745
+ return this
746
+ },
747
+
748
+ replace: function( regexp, newSubStr ) {
687
749
  var it = this
688
- var portionMode = portionMode || 'retain'
689
750
  it.finder.push(Finder( it.context, {
690
751
  find: regexp,
691
752
  replace: newSubStr,
692
753
  filterElements: function( currentNode ) {
693
- return it.filterElemFn( currentNode )
754
+ return it.filterFn( currentNode )
694
755
  },
695
- portionMode: portionMode
756
+ forceContext: function( currentNode ) {
757
+ return it.boundaryFn( currentNode )
758
+ },
759
+ portionMode: it.portionMode
696
760
  }))
697
761
  return it
698
762
  },
699
763
 
700
- wrap: function( regexp, strElemName, portionMode ) {
764
+ wrap: function( regexp, strElemName ) {
701
765
  var it = this
702
- var portionMode = portionMode || 'retain'
703
766
  it.finder.push(Finder( it.context, {
704
767
  find: regexp,
705
768
  wrap: strElemName,
706
769
  filterElements: function( currentNode ) {
707
- return it.filterElemFn( currentNode )
770
+ return it.filterFn( currentNode )
708
771
  },
709
- portionMode: portionMode
772
+ forceContext: function( currentNode ) {
773
+ return it.boundaryFn( currentNode )
774
+ },
775
+ portionMode: it.portionMode
710
776
  }))
711
777
  return it
712
778
  },
@@ -726,6 +792,10 @@ Fibre.fn = Fibre.prototype = {
726
792
  }
727
793
  }
728
794
 
795
+ // Deprecated API(s)
796
+ Fibre.fn.filterOut = Fibre.fn.avoid
797
+
798
+ // Make sure init() inherit from Fibre()
729
799
  Fibre.fn.init.prototype = Fibre.fn
730
800
 
731
801
  return Fibre
@@ -733,7 +803,7 @@ return Fibre
733
803
  }(
734
804
 
735
805
  /**
736
- * findAndReplaceDOMText v 0.4.2
806
+ * findAndReplaceDOMText v 0.4.3
737
807
  * @author James Padolsey http://james.padolsey.com
738
808
  * @license http://unlicense.org/UNLICENSE
739
809
  *
@@ -747,6 +817,7 @@ return Fibre
747
817
  var PORTION_MODE_FIRST = 'first'
748
818
  var doc = document
749
819
  var toString = {}.toString
820
+ var hasOwn = {}.hasOwnProperty
750
821
  function isArray(a) {
751
822
  return toString.call(a) == '[object Array]'
752
823
  }
@@ -822,13 +893,61 @@ return Fibre
822
893
  return new Finder(node, options)
823
894
  }
824
895
 
896
+ exposed.NON_PROSE_ELEMENTS = {
897
+ br:1, hr:1,
898
+ // Media / Source elements:
899
+ script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
900
+ // Input elements
901
+ input:1, textarea:1, select:1, option:1, optgroup: 1, button:1
902
+ }
903
+ exposed.NON_CONTIGUOUS_PROSE_ELEMENTS = {
904
+
905
+ // Elements that will not contain prose or block elements where we don't
906
+ // want prose to be matches across element borders:
907
+
908
+ // Block Elements
909
+ address:1, article:1, aside:1, blockquote:1, dd:1, div:1,
910
+ dl:1, fieldset:1, figcaption:1, figure:1, footer:1, form:1, h1:1, h2:1, h3:1,
911
+ h4:1, h5:1, h6:1, header:1, hgroup:1, hr:1, main:1, nav:1, noscript:1, ol:1,
912
+ output:1, p:1, pre:1, section:1, ul:1,
913
+ // Other misc. elements that are not part of continuous inline prose:
914
+ br:1, li: 1, summary: 1, dt:1, details:1, rp:1, rt:1, rtc:1,
915
+ // Media / Source elements:
916
+ script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
917
+ // Input elements
918
+ input:1, textarea:1, select:1, option:1, optgroup: 1, button:1,
919
+ // Table related elements:
920
+ table:1, tbody:1, thead:1, th:1, tr:1, td:1, caption:1, col:1, tfoot:1, colgroup:1
921
+
922
+ }
923
+ exposed.NON_INLINE_PROSE = function(el) {
924
+ return hasOwn.call(exposed.NON_CONTIGUOUS_PROSE_ELEMENTS, el.nodeName.toLowerCase())
925
+ }
926
+ // Presets accessed via `options.preset` when calling findAndReplaceDOMText():
927
+ exposed.PRESETS = {
928
+ prose: {
929
+ forceContext: exposed.NON_INLINE_PROSE,
930
+ filterElements: function(el) {
931
+ return !hasOwn.call(exposed.NON_PROSE_ELEMENTS, el.nodeName.toLowerCase())
932
+ }
933
+ }
934
+ }
825
935
  exposed.Finder = Finder
826
936
  /**
827
937
  * Finder -- encapsulates logic to find and replace.
828
938
  */
829
939
  function Finder(node, options) {
830
940
 
941
+ var preset = options.preset && exposed.PRESETS[options.preset]
831
942
  options.portionMode = options.portionMode || PORTION_MODE_RETAIN
943
+ if (preset) {
944
+ for (var i in preset) {
945
+ if (hasOwn.call(preset, i) && !hasOwn.call(options, i)) {
946
+ options[i] = preset[i]
947
+ }
948
+ }
949
+ }
950
+
832
951
  this.node = node
833
952
  this.options = options
834
953
  // ENable match-preparation method to be passed as option:
@@ -850,17 +969,34 @@ return Fibre
850
969
 
851
970
  var match
852
971
  var matchIndex = 0
972
+ var offset = 0
853
973
  var regex = this.options.find
854
- var text = this.getAggregateText()
974
+ var textAggregation = this.getAggregateText()
855
975
  var matches = []
976
+ var self = this
856
977
  regex = typeof regex === 'string' ? RegExp(escapeRegExp(regex), 'g') : regex
857
- if (regex.global) {
858
- while (match = regex.exec(text)) {
859
- matches.push(this.prepMatch(match, matchIndex++))
860
- }
861
- } else {
862
- if (match = text.match(regex)) {
863
- matches.push(this.prepMatch(match, 0))
978
+ matchAggregation(textAggregation)
979
+ function matchAggregation(textAggregation) {
980
+ for (var i = 0, l = textAggregation.length; i < l; ++i) {
981
+
982
+ var text = textAggregation[i]
983
+ if (typeof text !== 'string') {
984
+ // Deal with nested contexts: (recursive)
985
+ matchAggregation(text)
986
+ continue
987
+ }
988
+
989
+ if (regex.global) {
990
+ while (match = regex.exec(text)) {
991
+ matches.push(self.prepMatch(match, matchIndex++, offset))
992
+ }
993
+ } else {
994
+ if (match = text.match(regex)) {
995
+ matches.push(self.prepMatch(match, 0, offset))
996
+ }
997
+ }
998
+
999
+ offset += text.length
864
1000
  }
865
1001
  }
866
1002
 
@@ -870,14 +1006,14 @@ return Fibre
870
1006
  /**
871
1007
  * Prepares a single match with useful meta info:
872
1008
  */
873
- prepMatch: function(match, matchIndex) {
1009
+ prepMatch: function(match, matchIndex, characterOffset) {
874
1010
 
875
1011
  if (!match[0]) {
876
1012
  throw new Error('findAndReplaceDOMText cannot handle zero-length matches')
877
1013
  }
878
1014
 
879
- match.endIndex = match.index + match[0].length
880
- match.startIndex = match.index
1015
+ match.endIndex = characterOffset + match.index + match[0].length
1016
+ match.startIndex = characterOffset + match.index
881
1017
  match.index = matchIndex
882
1018
  return match
883
1019
  },
@@ -888,28 +1024,55 @@ return Fibre
888
1024
  getAggregateText: function() {
889
1025
 
890
1026
  var elementFilter = this.options.filterElements
1027
+ var forceContext = this.options.forceContext
891
1028
  return getText(this.node)
892
1029
  /**
893
1030
  * Gets aggregate text of a node without resorting
894
1031
  * to broken innerText/textContent
895
1032
  */
896
- function getText(node) {
1033
+ function getText(node, txt) {
897
1034
 
898
1035
  if (node.nodeType === 3) {
899
- return node.data
1036
+ return [node.data]
900
1037
  }
901
1038
 
902
1039
  if (elementFilter && !elementFilter(node)) {
903
- return ''
1040
+ return []
904
1041
  }
905
1042
 
906
- var txt = ''
1043
+ var txt = ['']
1044
+ var i = 0
907
1045
  if (node = node.firstChild) do {
908
- txt += getText(node)
1046
+
1047
+ if (node.nodeType === 3) {
1048
+ txt[i] += node.data
1049
+ continue
1050
+ }
1051
+
1052
+ var innerText = getText(node)
1053
+ if (
1054
+ forceContext &&
1055
+ node.nodeType === 1 &&
1056
+ (forceContext === true || forceContext(node))
1057
+ ) {
1058
+ txt[++i] = innerText
1059
+ txt[++i] = ''
1060
+ } else {
1061
+ if (typeof innerText[0] === 'string') {
1062
+ // Bridge nested text-node data so that they're
1063
+ // not considered their own contexts:
1064
+ // I.e. ['some', ['thing']] -> ['something']
1065
+ txt[i] += innerText.shift()
1066
+ }
1067
+ if (innerText.length) {
1068
+ txt[++i] = innerText
1069
+ txt[++i] = ''
1070
+ }
1071
+ }
909
1072
  } while (node = node.nextSibling)
910
1073
  return txt
911
1074
  }
912
-
1075
+
913
1076
  },
914
1077
 
915
1078
  /**
@@ -1197,15 +1360,34 @@ return Fibre
1197
1360
  }
1198
1361
  return exposed
1199
1362
  }())
1363
+
1200
1364
  );
1201
1365
 
1366
+ function createBdGroup( portion, match ) {
1367
+ var elem = $.create( 'h-char-group', 'biaodian cjk' )
1368
+
1369
+ if ( portion.index === 0 && portion.isEnd ) {
1370
+ elem.innerHTML = match[0]
1371
+ } else {
1372
+ elem.innerHTML = portion.text
1373
+ elem.classList.add( 'portion' )
1374
+
1375
+ if ( portion.index === 0 ) {
1376
+ elem.classList.add( 'isFirst' )
1377
+ } else if ( portion.isEnd ) {
1378
+ elem.classList.add( 'isEnd' )
1379
+ }
1380
+ }
1381
+ return elem
1382
+ }
1383
+
1202
1384
  function createBdChar( char ) {
1203
1385
  var div = $.create( 'div' )
1204
1386
  var unicode = char.charCodeAt( 0 ).toString( 16 )
1205
- var clazz = 'biaodian cjk ' + ( char.match( TYPESET.char.biaodian.open ) ? 'open' :
1206
- char.match( TYPESET.char.biaodian.close ) ? 'close end' :
1207
- char.match( TYPESET.char.biaodian.end ) ? 'end' :
1208
- char.match( new RegExp( '(' + UNICODE.biaodian.liga + ')' )) ? 'liga' : '' )
1387
+ var clazz = 'biaodian cjk ' + ( char.match( TYPESET.char.biaodian.open ) ? 'bd-open' :
1388
+ char.match( TYPESET.char.biaodian.close ) ? 'bd-close bd-end' :
1389
+ char.match( TYPESET.char.biaodian.end ) ? 'bd-end' :
1390
+ char.match( new RegExp( '(' + UNICODE.biaodian.liga + ')' )) ? 'bd-liga' : '' )
1209
1391
 
1210
1392
  div.innerHTML = '<h-char unicode="' + unicode + '" class="' + clazz + '">' + char + '</h-char>'
1211
1393
  return div.firstChild
@@ -1213,18 +1395,17 @@ function createBdChar( char ) {
1213
1395
 
1214
1396
  $.extend( Fibre.fn, {
1215
1397
  // Force punctuation & biaodian typesetting rules to be applied.
1216
- jinzify: function() {
1217
- var origFilterOutSelector = this.filterOutSelector
1218
- this.filterOutSelector += ', h-jinze'
1219
-
1398
+ jinzify: function( selector ) {
1399
+ return (
1220
1400
  this
1401
+ .filter( selector || null )
1402
+ .avoid( 'h-jinze' )
1221
1403
  .replace(
1222
1404
  TYPESET.jinze.touwei,
1223
1405
  function( portion, match ) {
1224
1406
  var elem = $.create( 'h-jinze', 'touwei' )
1225
1407
  elem.innerHTML = match[0]
1226
- return (( portion.index === 0 && portion.isEnd ) || portion.index === 1 )
1227
- ? elem : ''
1408
+ return (( portion.index === 0 && portion.isEnd ) || portion.index === 1 ) ? elem : ''
1228
1409
  }
1229
1410
  )
1230
1411
  .replace(
@@ -1253,13 +1434,12 @@ $.extend( Fibre.fn, {
1253
1434
  ? elem : ''
1254
1435
  }
1255
1436
  )
1256
-
1257
- this.filterOutSelector = origFilterOutSelector
1258
- return this
1437
+ .endAvoid()
1438
+ .endFilter()
1439
+ )
1259
1440
  },
1260
1441
 
1261
1442
  groupify: function( option ) {
1262
- var origFilterOutSelector = this.filterOutSelector
1263
1443
  var option = $.extend({
1264
1444
  biaodian: false,
1265
1445
  //punct: false,
@@ -1269,16 +1449,16 @@ $.extend( Fibre.fn, {
1269
1449
  western: false // Includes Latin, Greek and Cyrillic
1270
1450
  }, option || {})
1271
1451
 
1272
- this.filterOutSelector += ', h-hangable, h-char-group'
1452
+ this.avoid( 'h-hangable, h-char-group, h-word' )
1273
1453
 
1274
1454
  if ( option.biaodian ) {
1275
- this.wrap(
1276
- TYPESET.group.biaodian[ 0 ], $.clone( $.create( 'h-char-group', 'biaodian cjk' ))
1277
- ).wrap(
1278
- TYPESET.group.biaodian[ 1 ], $.clone( $.create( 'h-char-group', 'biaodian cjk' ))
1455
+ this.replace(
1456
+ TYPESET.group.biaodian[ 0 ], createBdGroup
1457
+ ).replace(
1458
+ TYPESET.group.biaodian[ 1 ], createBdGroup
1279
1459
  )
1280
1460
  }
1281
- if ( option.hanzi ) {
1461
+ if ( option.hanzi || option.cjk ) {
1282
1462
  this.wrap(
1283
1463
  TYPESET.group.hanzi, $.clone( $.create( 'h-char-group', 'hanzi cjk' ))
1284
1464
  )
@@ -1293,20 +1473,17 @@ $.extend( Fibre.fn, {
1293
1473
  TYPESET.group.kana, $.clone( $.create( 'h-char-group', 'kana' ))
1294
1474
  )
1295
1475
  }
1296
- if ( option.eonmun ) {
1476
+ if ( option.eonmun || option.hangul ) {
1297
1477
  this.wrap(
1298
1478
  TYPESET.group.eonmun, $.clone( $.create( 'h-word', 'eonmun hangul' ))
1299
1479
  )
1300
1480
  }
1301
1481
 
1302
- this.filterOutSelector = origFilterOutSelector
1482
+ this.endAvoid()
1303
1483
  return this
1304
1484
  },
1305
1485
 
1306
- // Implementation of character-level selector
1307
- // (字元級選擇器)
1308
1486
  charify: function( option ) {
1309
- var origFilterOutSelector = this.filterOutSelector
1310
1487
  var option = $.extend({
1311
1488
  biaodian: false,
1312
1489
  punct: false,
@@ -1318,7 +1495,7 @@ $.extend( Fibre.fn, {
1318
1495
  eonmun: false
1319
1496
  }, option || {})
1320
1497
 
1321
- this.filterOutSelector += ', h-char'
1498
+ this.avoid( 'h-char' )
1322
1499
 
1323
1500
  if ( option.biaodian ) {
1324
1501
  this.replace(
@@ -1329,7 +1506,7 @@ $.extend( Fibre.fn, {
1329
1506
  function( portion, match ) { return createBdChar( match[0] ) }
1330
1507
  )
1331
1508
  }
1332
- if ( option.hanzi ) {
1509
+ if ( option.hanzi || option.cjk ) {
1333
1510
  this.wrap(
1334
1511
  TYPESET.char.hanzi, $.clone( $.create( 'h-char', 'hanzi cjk' ))
1335
1512
  )
@@ -1344,12 +1521,12 @@ $.extend( Fibre.fn, {
1344
1521
  TYPESET.char.latin, $.clone( $.create( 'h-char', 'alphabet latin' ))
1345
1522
  )
1346
1523
  }
1347
- if ( option.ellinika ) {
1524
+ if ( option.ellinika || option.greek ) {
1348
1525
  this.wrap(
1349
1526
  TYPESET.char.ellinika, $.clone( $.create( 'h-char', 'alphabet ellinika greek' ))
1350
1527
  )
1351
1528
  }
1352
- if ( option.kirillica ) {
1529
+ if ( option.kirillica || option.cyrillic ) {
1353
1530
  this.wrap(
1354
1531
  TYPESET.char.kirillica, $.clone( $.create( 'h-char', 'alphabet kirillica cyrillic' ))
1355
1532
  )
@@ -1359,13 +1536,13 @@ $.extend( Fibre.fn, {
1359
1536
  TYPESET.char.kana, $.clone( $.create( 'h-char', 'kana' ))
1360
1537
  )
1361
1538
  }
1362
- if ( option.eonmun ) {
1539
+ if ( option.eonmun || option.hangul ) {
1363
1540
  this.wrap(
1364
1541
  TYPESET.char.eonmun, $.clone( $.create( 'h-char', 'eonmun hangul' ))
1365
1542
  )
1366
1543
  }
1367
1544
 
1368
- this.filterOutSelector = origFilterOutSelector
1545
+ this.endAvoid()
1369
1546
  return this
1370
1547
  }
1371
1548
  })
@@ -1373,12 +1550,12 @@ $.extend( Fibre.fn, {
1373
1550
  Han.find = Fibre
1374
1551
 
1375
1552
  void [
1376
- 'replace',
1377
- 'wrap',
1378
- 'revert',
1379
- 'jinzify',
1380
- 'groupify',
1381
- 'charify'
1553
+ 'setMode',
1554
+ 'wrap', 'replace', 'revert',
1555
+ 'addBoundary', 'removeBoundary',
1556
+ 'avoid', 'endAvoid',
1557
+ 'filter', 'endFilter',
1558
+ 'jinzify', 'groupify', 'charify'
1382
1559
  ].forEach(function( method ) {
1383
1560
  Han.fn[ method ] = function() {
1384
1561
  if ( !this.finder ) {
@@ -1477,7 +1654,6 @@ Locale.support = (function() {
1477
1654
  // Create an element for feature detecting
1478
1655
  // (in `testCSSProp`)
1479
1656
  var elem = $.create( 'h-test' )
1480
- var exposed = {}
1481
1657
 
1482
1658
  function testCSSProp( prop ) {
1483
1659
  var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1)
@@ -1496,7 +1672,7 @@ Locale.support = (function() {
1496
1672
  var fakeBody = body || $.create( 'body' )
1497
1673
  var div = $.create( 'div' )
1498
1674
  var container = body ? div : fakeBody
1499
- var callback = typeof callback === 'function' ? callback : function() {}
1675
+ var callback = typeof callback === 'function' ? callback : function() {}
1500
1676
  var style, ret, docOverflow
1501
1677
 
1502
1678
  style = [ '<style>', rule, '</style>' ].join('')
@@ -1537,6 +1713,29 @@ Locale.support = (function() {
1537
1713
  }
1538
1714
 
1539
1715
  return {
1716
+ columnwidth: testCSSProp( 'columnWidth' ),
1717
+
1718
+ fontface: (function() {
1719
+ var ret
1720
+
1721
+ injectElementWithStyle(
1722
+ '@font-face { font-family: font; src: url("//"); }',
1723
+ function( node, rule ) {
1724
+ var style = $.qsa( 'style', node )[0]
1725
+ var sheet = style.sheet || style.styleSheet
1726
+ var cssText = sheet ?
1727
+ ( sheet.cssRules && sheet.cssRules[0] ?
1728
+ sheet.cssRules[0].cssText : sheet.cssText || ''
1729
+ ) : ''
1730
+
1731
+ ret = /src/i.test( cssText ) &&
1732
+ cssText.indexOf( rule.split(' ')[0] ) === 0
1733
+ }
1734
+ )
1735
+
1736
+ return ret
1737
+ })(),
1738
+
1540
1739
  ruby: (function() {
1541
1740
  var ruby = $.create( 'ruby' )
1542
1741
  var rt = $.create( 'rt' )
@@ -1550,8 +1749,7 @@ Locale.support = (function() {
1550
1749
  // Browsers that support ruby hide the `<rp>` via `display: none`
1551
1750
  ret = (
1552
1751
  getStyle( rp, 'display' ) === 'none' ||
1553
- // but in IE, `<rp>` has `display: inline`
1554
- // so, the test needs other conditions:
1752
+ // but in IE, `<rp>` has `display: inline`, so the test needs other conditions:
1555
1753
  getStyle( ruby, 'display' ) === 'ruby' &&
1556
1754
  getStyle( rt, 'display' ) === 'ruby-text'
1557
1755
  ) ? true : false
@@ -1565,27 +1763,25 @@ Locale.support = (function() {
1565
1763
  return ret
1566
1764
  })(),
1567
1765
 
1568
- fontface: (function() {
1569
- var ret
1766
+ 'ruby-display': (function() {
1767
+ var div = $.create( 'div' )
1570
1768
 
1571
- injectElementWithStyle(
1572
- '@font-face { font-family: font; src: url("//"); }',
1573
- function( node, rule ) {
1574
- var style = $.qsa( 'style', node )[0]
1575
- var sheet = style.sheet || style.styleSheet
1576
- var cssText = sheet ?
1577
- ( sheet.cssRules && sheet.cssRules[0] ?
1578
- sheet.cssRules[0].cssText : sheet.cssText || ''
1579
- ) : ''
1769
+ div.innerHTML = '<h-test-a style="display: ruby;"></h-test-a><h-test-b style="display: ruby-text-container;"></h-test-b>'
1770
+ return div.querySelector( 'h-test-a' ).style.display === 'ruby' && div.querySelector( 'h-test-b' ).style.display === 'ruby-text-container'
1771
+ })(),
1580
1772
 
1581
- ret = /src/i.test( cssText ) &&
1582
- cssText.indexOf( rule.split(' ')[0] ) === 0
1583
- }
1584
- )
1773
+ 'ruby-interchar': (function() {
1774
+ var IC = 'inter-character'
1775
+ var div = $.create( 'div' )
1776
+ var css
1585
1777
 
1586
- return ret
1778
+ div.innerHTML = '<h-test style="-moz-ruby-position:' + IC + ';-ms-ruby-position:' + IC + ';-webkit-ruby-position:' + IC + ';ruby-position:' + IC + ';"></h-test>'
1779
+ css = div.querySelector( 'h-test' ).style
1780
+ return css.rubyPosition === IC || css.WebkitRubyPosition === IC || css.MozRubyPosition === IC || css.msRubyPosition === IC
1587
1781
  })(),
1588
1782
 
1783
+ textemphasis: testCSSProp( 'textEmphasis' ),
1784
+
1589
1785
  // Address feature support test for `unicode-range` via
1590
1786
  // detecting whether it's Arial (supported) or
1591
1787
  // Times New Roman (not supported).
@@ -1605,19 +1801,10 @@ Locale.support = (function() {
1605
1801
  return ret
1606
1802
  })(),
1607
1803
 
1608
- columnwidth: testCSSProp( 'columnWidth' ),
1609
-
1610
- textemphasis: testCSSProp( 'textEmphasis' ),
1611
-
1612
1804
  writingmode: testCSSProp( 'writingMode' )
1613
1805
  }
1614
1806
  })()
1615
1807
 
1616
- var UA = window.navigator.userAgent || null
1617
- var isIE = /Trident/i.test( UA )
1618
-
1619
- Locale.support['pseudo-element-clipboard'] = isIE ? true : false
1620
-
1621
1808
  Locale.initCond = function( target ) {
1622
1809
  var target = target || root
1623
1810
  var ret = ''
@@ -1632,6 +1819,195 @@ Locale.initCond = function( target ) {
1632
1819
  return ret
1633
1820
  }
1634
1821
 
1822
+ var SUPPORT_IC = Locale.support[ 'ruby-interchar' ]
1823
+
1824
+ // 1. Simple ruby polyfill;
1825
+ // 2. Inter-character polyfill for Zhuyin
1826
+ function renderSimpleRuby( $ruby ) {
1827
+ var frag = $.create( '!' )
1828
+ var clazz = $ruby.classList
1829
+ var $rb, $ru
1830
+
1831
+ frag.appendChild( $.clone( $ruby ))
1832
+
1833
+ $
1834
+ .tag( 'rt', frag.firstChild )
1835
+ .forEach(function( $rt ) {
1836
+ var $rb = $.create( '!' )
1837
+ var airb = []
1838
+ var irb
1839
+
1840
+ // Consider the previous nodes the implied
1841
+ // ruby base
1842
+ do {
1843
+ irb = ( irb || $rt ).previousSibling
1844
+ if ( !irb || irb.nodeName.match( /((?:h\-)?r[ubt])/i )) break
1845
+
1846
+ $rb.insertBefore( $.clone( irb ), $rb.firstChild )
1847
+ airb.push( irb )
1848
+ } while ( !irb.nodeName.match( /((?:h\-)?r[ubt])/i ))
1849
+
1850
+ // Create a real `<h-ru>` to append.
1851
+ $ru = clazz.contains( 'zhuyin' ) ? createZhuyinRu( $rb, $rt ) : createNormalRu( $rb, $rt )
1852
+
1853
+ // Replace the ruby text with the new `<h-ru>`,
1854
+ // and remove the original implied ruby base(s)
1855
+ try {
1856
+ $rt.parentNode.replaceChild( $ru, $rt )
1857
+ airb.map( $.remove )
1858
+ } catch ( e ) {}
1859
+ })
1860
+ return createCustomRuby( frag )
1861
+ }
1862
+
1863
+ function renderInterCharRuby( $ruby ) {
1864
+ var frag = $.create( '!' )
1865
+ frag.appendChild( $.clone( $ruby ))
1866
+
1867
+ $
1868
+ .tag( 'rt', frag.firstChild )
1869
+ .forEach(function( $rt ) {
1870
+ var $rb = $.create( '!' )
1871
+ var airb = []
1872
+ var irb, $zhuyin
1873
+
1874
+ // Consider the previous nodes the implied
1875
+ // ruby base
1876
+ do {
1877
+ irb = ( irb || $rt ).previousSibling
1878
+ if ( !irb || irb.nodeName.match( /((?:h\-)?r[ubt])/i )) break
1879
+
1880
+ $rb.insertBefore( $.clone( irb ), $rb.firstChild )
1881
+ airb.push( irb )
1882
+ } while ( !irb.nodeName.match( /((?:h\-)?r[ubt])/i ))
1883
+
1884
+ $zhuyin = $.create( 'rt' )
1885
+ $zhuyin.innerHTML = getZhuyinHTML( $rt )
1886
+ $rt.parentNode.replaceChild( $zhuyin, $rt )
1887
+ })
1888
+ return frag.firstChild
1889
+ }
1890
+
1891
+ // 3. Complex ruby polyfill
1892
+ // - Double-lined annotation;
1893
+ // - Right-angled annotation.
1894
+ function renderComplexRuby( $ruby ) {
1895
+ var frag = $.create( '!' )
1896
+ var clazz = $ruby.classList
1897
+ var $cloned, $rb, $ru, maxspan
1898
+
1899
+ frag.appendChild( $.clone( $ruby ))
1900
+ $cloned = frag.firstChild
1901
+
1902
+ $rb = $ru = $.tag( 'rb', $cloned )
1903
+ maxspan = $rb.length
1904
+
1905
+ // First of all, deal with Zhuyin containers
1906
+ // individually
1907
+ //
1908
+ // Note that we only support one single Zhuyin
1909
+ // container in each complex ruby
1910
+ void function( $rtc ) {
1911
+ if ( !$rtc ) return
1912
+
1913
+ $ru = $
1914
+ .tag( 'rt', $rtc )
1915
+ .map(function( $rt, i ) {
1916
+ if ( !$rb[ i ] ) return
1917
+ var ret = createZhuyinRu( $rb[ i ], $rt )
1918
+
1919
+ try {
1920
+ $rb[ i ].parentNode.replaceChild( ret, $rb[ i ] )
1921
+ } catch ( e ) {}
1922
+ return ret
1923
+ })
1924
+
1925
+ // Remove the container once it's useless
1926
+ $.remove( $rtc )
1927
+ $cloned.setAttribute( 'rightangle', 'true' )
1928
+ }( $cloned.querySelector( 'rtc.zhuyin' ))
1929
+
1930
+ // Then, normal annotations other than Zhuyin
1931
+ $
1932
+ .qsa( 'rtc:not(.zhuyin)', $cloned )
1933
+ .forEach(function( $rtc, order ) {
1934
+ var ret
1935
+ ret = $
1936
+ .tag( 'rt', $rtc )
1937
+ .map(function( $rt, i ) {
1938
+ var rbspan = Number( $rt.getAttribute( 'rbspan' ) || 1 )
1939
+ var span = 0
1940
+ var aRb = []
1941
+ var $rb, ret
1942
+
1943
+ if ( rbspan > maxspan ) rbspan = maxspan
1944
+
1945
+ do {
1946
+ try {
1947
+ $rb = $ru.shift()
1948
+ aRb.push( $rb )
1949
+ } catch (e) {}
1950
+
1951
+ if ( typeof $rb === 'undefined' ) break
1952
+ span += Number( $rb.getAttribute( 'span' ) || 1 )
1953
+ } while ( rbspan > span )
1954
+
1955
+ if ( rbspan < span ) {
1956
+ if ( aRb.length > 1 ) {
1957
+ console.error( 'An impossible `rbspan` value detected.', ruby )
1958
+ return
1959
+ }
1960
+ aRb = $.tag( 'rb', aRb[0] )
1961
+ $ru = aRb.slice( rbspan ).concat( $ru )
1962
+ aRb = aRb.slice( 0, rbspan )
1963
+ span = rbspan
1964
+ }
1965
+
1966
+ ret = createNormalRu( aRb, $rt, {
1967
+ 'class': clazz,
1968
+ span: span,
1969
+ order: order
1970
+ })
1971
+
1972
+ try {
1973
+ aRb[0].parentNode.replaceChild( ret, aRb.shift() )
1974
+ aRb.map( $.remove )
1975
+ } catch (e) {}
1976
+ return ret
1977
+ })
1978
+ $ru = ret
1979
+ if ( order === 1 ) $cloned.setAttribute( 'doubleline', 'true' )
1980
+
1981
+ // Remove the container once it's useless
1982
+ $.remove( $rtc )
1983
+ })
1984
+ return createCustomRuby( frag )
1985
+ }
1986
+
1987
+ // Create a new fake `<h-ruby>` element so the
1988
+ // style sheets will render it as a polyfill,
1989
+ // which also helps to avoid the UA style.
1990
+ function createCustomRuby( frag ) {
1991
+ var $ruby = frag.firstChild
1992
+ var hruby = $.create( 'h-ruby' )
1993
+
1994
+ hruby.innerHTML = $ruby.innerHTML
1995
+ $.setAttr( hruby, $ruby.attributes )
1996
+ hruby.normalize()
1997
+ return hruby
1998
+ }
1999
+
2000
+ function simplifyRubyClass( elem ) {
2001
+ if ( !elem instanceof Element ) return elem
2002
+ var clazz = elem.classList
2003
+
2004
+ if ( clazz.contains( 'pinyin' )) clazz.add( 'romanization' )
2005
+ else if ( clazz.contains( 'romanization' )) clazz.add( 'annotation' )
2006
+ else if ( clazz.contains( 'mps' )) clazz.add( 'zhuyin' )
2007
+ else if ( clazz.contains( 'rightangle' )) clazz.add( 'complex' )
2008
+ return elem
2009
+ }
2010
+
1635
2011
  /**
1636
2012
  * Create and return a new `<h-ru>` element
1637
2013
  * according to the given contents
@@ -1640,7 +2016,7 @@ function createNormalRu( $rb, $rt, attr ) {
1640
2016
  var $ru = $.create( 'h-ru' )
1641
2017
  var $rt = $.clone( $rt )
1642
2018
  var attr = attr || {}
1643
- attr.annotation = $rt.textContent
2019
+ attr.annotation = 'true'
1644
2020
 
1645
2021
  if ( Array.isArray( $rb )) {
1646
2022
  $ru.innerHTML = $rb.map(function( rb ) {
@@ -1657,7 +2033,7 @@ function createNormalRu( $rb, $rt, attr ) {
1657
2033
  }
1658
2034
 
1659
2035
  /**
1660
- * Create and return a new `<ru>` element
2036
+ * Create and return a new `<h-ru>` element
1661
2037
  * in Zhuyin form
1662
2038
  */
1663
2039
  function createZhuyinRu( $rb, $rt ) {
@@ -1665,15 +2041,31 @@ function createZhuyinRu( $rb, $rt ) {
1665
2041
 
1666
2042
  // Create an element to return
1667
2043
  var $ru = $.create( 'h-ru' )
2044
+ $ru.setAttribute( 'zhuyin', true )
1668
2045
 
2046
+ // - <h-ru zhuyin>
2047
+ // - <rb><rb/>
2048
+ // - <h-zhuyin>
2049
+ // - <h-yin></h-yin>
2050
+ // - <h-diao></h-diao>
2051
+ // - </h-zhuyin>
2052
+ // - </h-ru>
2053
+ $ru.appendChild( $rb )
2054
+ $ru.innerHTML += getZhuyinHTML( $rt )
2055
+ return $ru
2056
+ }
2057
+
2058
+ /**
2059
+ * Create a Zhuyin-form HTML string
2060
+ */
2061
+ function getZhuyinHTML( rt ) {
1669
2062
  // #### Explanation ####
1670
2063
  // * `zhuyin`: the entire phonetic annotation
1671
2064
  // * `yin`: the plain pronunciation (w/out tone)
1672
2065
  // * `diao`: the tone
1673
- // * `form`: the combination of the pronunciation
1674
- // * `len`: the text length of `yin`
1675
- var zhuyin = $rt.textContent
1676
- var yin, diao, form, len
2066
+ // * `len`: the length of the plain pronunciation (`yin`)
2067
+ var zhuyin = typeof rt === 'string' ? rt : rt.textContent
2068
+ var yin, diao, len
1677
2069
 
1678
2070
  yin = zhuyin.replace( TYPESET.zhuyin.diao, '' )
1679
2071
  len = yin ? yin.length : 0
@@ -1681,35 +2073,48 @@ function createZhuyinRu( $rb, $rt ) {
1681
2073
  .replace( yin, '' )
1682
2074
  .replace( /[\u02C5]/g, '\u02C7' )
1683
2075
  .replace( /[\u030D]/g, '\u0358' )
1684
-
1685
- form = zhuyin.replace( TYPESET.zhuyin.form, function( s, j, y ) {
1686
- return [
1687
- s ? 'S' : null,
1688
- j ? 'J' : null,
1689
- y ? 'Y' : null
1690
- ].join('')
1691
- })
1692
- // - <h-ru>
1693
- // - <rb><rb/>
1694
- // - <h-zhuyin>
1695
- // - <h-yin></h-yin>
1696
- // - <h-diao></h-diao>
1697
- // - </h-zhuyin>
1698
- // - </h-ru>
1699
- $ru.appendChild( $rb )
1700
- $ru.innerHTML += '<h-zhuyin><h-yin>' + yin + '</h-yin><h-diao>' + diao + '</h-diao></h-zhuyin>'
1701
-
1702
- // Finally, set up the necessary attribute
1703
- // and return the new `<ru>`
1704
- $.setAttr( $ru, {
1705
- zhuyin: '',
1706
- diao: diao,
1707
- length: len,
1708
- form: form
1709
- })
1710
- return $ru
2076
+ return len === 0 ? '' : '<h-zhuyin length="' + len + '" diao="' + diao + '"><h-yin>' + yin + '</h-yin><h-diao>' + diao + '</h-diao></h-zhuyin>'
1711
2077
  }
1712
2078
 
2079
+ /**
2080
+ * Normalize `ruby` elements
2081
+ */
2082
+ $.extend( Locale, {
2083
+
2084
+ // Address normalisation for both simple and complex
2085
+ // rubies (interlinear annotations)
2086
+ renderRuby: function( context, target ) {
2087
+ var target = target || 'ruby'
2088
+ var $target = $.qsa( target, context )
2089
+
2090
+ $.qsa( 'rtc', context )
2091
+ .concat( $target ).map( simplifyRubyClass )
2092
+
2093
+ $target
2094
+ .forEach(function( $ruby ) {
2095
+ var clazz = $ruby.classList
2096
+ var $new
2097
+
2098
+ if ( clazz.contains( 'complex' )) $new = renderComplexRuby( $ruby )
2099
+ else if ( clazz.contains( 'zhuyin' )) $new = SUPPORT_IC ? renderInterCharRuby( $ruby ) : renderSimpleRuby( $ruby )
2100
+
2101
+ // Finally, replace it
2102
+ if ( $new ) $ruby.parentNode.replaceChild( $new, $ruby )
2103
+ })
2104
+ },
2105
+
2106
+ simplifyRubyClass: simplifyRubyClass,
2107
+ getZhuyinHTML: getZhuyinHTML,
2108
+ renderComplexRuby: renderComplexRuby,
2109
+ renderSimpleRuby: renderSimpleRuby,
2110
+ renderInterCharRuby: renderInterCharRuby
2111
+
2112
+ // ### TODO list ###
2113
+ //
2114
+ // * Debug mode
2115
+ // * Better error-tolerance
2116
+ })
2117
+
1713
2118
  /**
1714
2119
  * Normalisation rendering mechanism
1715
2120
  */
@@ -1761,221 +2166,26 @@ $.extend( Locale, {
1761
2166
  .forEach(function( elem ) {
1762
2167
  var $elem = Han( elem )
1763
2168
 
1764
- if ( !Locale.support.textemphasis ) {
2169
+ if ( Locale.support.textemphasis ) {
2170
+ $elem
2171
+ .avoid( 'rt, h-char, h-char-group' )
2172
+ .charify({ biaodian: true, punct: true })
2173
+ } else {
1765
2174
  $elem
2175
+ .avoid( 'rt, h-char, h-char-group' )
1766
2176
  .jinzify()
1767
- .groupify({ western: true })
1768
- }
1769
-
1770
- $elem
1771
- .groupify({ biaodian: true })
1772
- .charify( Locale.support.textemphasis ? {
1773
- biaodian: true,
1774
- punct: true
1775
- } : {
1776
- hanzi: true,
1777
- biaodian: true,
1778
- punct: true,
1779
- latin: true,
1780
- ellinika: true,
1781
- kirillica: true
1782
- })
1783
- })
1784
- },
1785
-
1786
- // Address normalisation for both simple and complex
1787
- // rubies
1788
- renderRuby: function( context, target ) {
1789
- var method = target ? 'qsa' : 'tag'
1790
- var target = target || 'ruby'
1791
- var $target = $[ method ]( target, context )
1792
- var $simpClaElem = $.qsa( target + ', rtc', context )
1793
-
1794
- // First of all, simplify semantic classes
1795
- $simpClaElem
1796
- .forEach(function( elem ) {
1797
- var clazz = elem.classList
1798
-
1799
- if ( clazz.contains( 'pinyin' )) {
1800
- clazz.add( 'romanization' )
1801
- } else if ( clazz.contains( 'mps' )) {
1802
- clazz.add( 'zhuyin' )
1803
- }
1804
-
1805
- if ( clazz.contains( 'romanization' )) {
1806
- clazz.add( 'annotation' )
1807
- }
1808
- })
1809
-
1810
- // Deal with `<ruby>`
1811
- $target
1812
- .forEach(function( ruby ) {
1813
- var clazz = ruby.classList
1814
-
1815
- var condition = (
1816
- !Locale.support.ruby ||
1817
- clazz.contains( 'zhuyin') ||
1818
- clazz.contains( 'complex' ) ||
1819
- clazz.contains( 'rightangle' )
1820
- )
1821
-
1822
- var frag, $cloned, $rb, $ru, maxspan, hruby
1823
-
1824
- if ( !condition ) return
1825
-
1826
- // Apply document fragment here to avoid
1827
- // continuously pointless re-paint
1828
- frag = $.create( '!' )
1829
- frag.appendChild( $.clone( ruby ))
1830
- $cloned = $.qsa( target, frag )[0]
1831
-
1832
- // 1. Simple ruby polyfill for, um, Firefox;
1833
- // 2. Zhuyin polyfill for all browsers.
1834
- if ( !Locale.support.ruby || clazz.contains( 'zhuyin' )) {
1835
-
1836
- $
1837
- .tag( 'rt', $cloned )
1838
- .forEach(function( rt ) {
1839
- var $rb = $.create( '!' )
1840
- var airb = []
1841
- var irb
1842
-
1843
- // Consider the previous nodes the implied
1844
- // ruby base
1845
- do {
1846
- irb = ( irb || rt ).previousSibling
1847
-
1848
- if ( !irb || irb.nodeName.match( /(r\-?[ubt])/i )) break
1849
-
1850
- $rb.insertBefore( $.clone( irb ), $rb.firstChild )
1851
- airb.push( irb )
1852
- } while ( !irb.nodeName.match( /(r\-?[ubt])/i ))
1853
- // Create a real `<h-ru>` to append.
1854
- $ru = clazz.contains( 'zhuyin' ) ?
1855
- createZhuyinRu( $rb, rt ) : createNormalRu( $rb, rt )
1856
-
1857
- // Replace the ruby text with the new `<h-ru>`,
1858
- // and remove the original implied ruby base(s)
1859
- try {
1860
- rt.parentNode.replaceChild( $ru, rt )
1861
-
1862
- airb
1863
- .forEach(function( irb ) {
1864
- $.remove( irb )
1865
- })
1866
- } catch ( e ) {}
2177
+ .groupify({ western: true, biaodian: true })
2178
+ .charify({
2179
+ hanzi: true,
2180
+ biaodian: true,
2181
+ punct: true,
2182
+ latin: true,
2183
+ ellinika: true,
2184
+ kirillica: true
1867
2185
  })
1868
2186
  }
1869
-
1870
- // 3. Complex ruby polyfill
1871
- // - Double-lined annotation;
1872
- // - Right-angled annotation.
1873
- if ( clazz.contains( 'complex' ) || clazz.contains( 'rightangle' )) {
1874
- $rb = $ru = $.tag( 'rb', $cloned )
1875
- maxspan = $rb.length
1876
-
1877
- // First of all, deal with Zhuyin containers
1878
- // individually
1879
- //
1880
- // Note that we only support one single Zhuyin
1881
- // container in each complex ruby
1882
- void function( rtc ) {
1883
- if ( !rtc ) return
1884
-
1885
- $ru = $
1886
- .tag( 'rt', rtc )
1887
- .map(function( rt, i ) {
1888
- if ( !$rb[ i ] ) return
1889
- var ret = createZhuyinRu( $rb[ i ], rt )
1890
-
1891
- try {
1892
- $rb[ i ].parentNode.replaceChild( ret, $rb[ i ] )
1893
- } catch ( e ) {}
1894
- return ret
1895
- })
1896
-
1897
- // Remove the container once it's useless
1898
- $.remove( rtc )
1899
- ruby.setAttribute( 'rightangle', '' )
1900
- }( $cloned.querySelector( 'rtc.zhuyin' ))
1901
-
1902
- // Then, normal annotations other than Zhuyin
1903
- $
1904
- .qsa( 'rtc:not(.zhuyin)', $cloned )
1905
- .forEach(function( rtc, order ) {
1906
- var ret
1907
- ret = $
1908
- .tag( 'rt', rtc )
1909
- .map(function( rt, i ) {
1910
- var rbspan = Number( rt.getAttribute( 'rbspan' ) || 1 )
1911
- var span = 0
1912
- var aRb = []
1913
- var rb, ret
1914
-
1915
- if ( rbspan > maxspan ) {
1916
- rbspan = maxspan
1917
- }
1918
-
1919
- do {
1920
- try {
1921
- rb = $ru.shift()
1922
- aRb.push( rb )
1923
- } catch (e) {}
1924
-
1925
- if ( typeof rb === 'undefined' ) break
1926
- span += Number( rb.getAttribute( 'span' ) || 1 )
1927
- } while ( rbspan > span )
1928
-
1929
- if ( rbspan < span ) {
1930
- if ( aRb.length > 1 ) {
1931
- console.error( 'An impossible `rbspan` value detected.', ruby )
1932
- return
1933
- }
1934
- aRb = $.tag( 'h-rb', aRb[0] )
1935
- $ru = aRb.slice( rbspan ).concat( $ru )
1936
- aRb = aRb.slice( 0, rbspan )
1937
- span = rbspan
1938
- }
1939
-
1940
- ret = createNormalRu( aRb, rt, {
1941
- 'class': clazz,
1942
- span: span,
1943
- order: order
1944
- })
1945
-
1946
- try {
1947
- aRb[0].parentNode.replaceChild( ret, aRb.shift())
1948
- aRb.forEach(function( rb ) {
1949
- $.remove( rb )
1950
- })
1951
- } catch (e) {}
1952
-
1953
- return ret
1954
- })
1955
- $ru = ret
1956
- // Remove the container once it's useless
1957
- $.remove( rtc )
1958
- })
1959
- }
1960
- // Create a new fake `<h-ruby>` element so the
1961
- // style sheets will render it as a polyfill,
1962
- // which also helps to avoid the UA style.
1963
- hruby = $.create( 'h-ruby' )
1964
- hruby.innerHTML = frag.firstChild.innerHTML
1965
-
1966
- // Copy all attributes onto it
1967
- $.setAttr( hruby, ruby.attributes )
1968
- hruby.normalize()
1969
-
1970
- // Finally, replace it
1971
- ruby.parentNode.replaceChild( hruby, ruby )
1972
2187
  })
1973
2188
  }
1974
-
1975
- // ### TODO list ###
1976
- //
1977
- // * Debug mode
1978
- // * Better error-tolerance
1979
2189
  })
1980
2190
 
1981
2191
  Han.normalize = Locale
@@ -2049,9 +2259,9 @@ $.extend( Han, {
2049
2259
  // Elements to be filtered according to the
2050
2260
  // HWS rendering mode
2051
2261
  if ( strict ) {
2052
- finder.filterOut( 'textarea, code, kbd, samp, pre', true )
2262
+ finder.avoid( 'textarea, code, kbd, samp, pre' )
2053
2263
  } else {
2054
- finder.filterOut( 'textarea', true )
2264
+ finder.avoid( 'textarea' )
2055
2265
  }
2056
2266
 
2057
2267
  finder
@@ -2138,7 +2348,7 @@ $.extend( Han.fn, {
2138
2348
  }
2139
2349
  })
2140
2350
 
2141
- Han.isSpaceFontLoaded = (function() {
2351
+ function detectSpaceFont() {
2142
2352
  var div = $.create( 'div' )
2143
2353
  var ret
2144
2354
 
@@ -2147,23 +2357,26 @@ Han.isSpaceFontLoaded = (function() {
2147
2357
  ret = div.firstChild.offsetWidth !== div.lastChild.offsetWidth
2148
2358
  $.remove( div, body )
2149
2359
  return ret
2150
- })()
2360
+ }
2151
2361
 
2152
- Han.support['han-space'] = Han.isSpaceFontLoaded
2362
+ $.extend( Han, {
2363
+ detectSpaceFont: detectSpaceFont,
2364
+ isSpaceFontLoaded: detectSpaceFont()
2365
+ })
2366
+
2367
+ Han.support['han-space'] = detectSpaceFont()
2153
2368
 
2154
2369
  Han.renderHanging = function( context ) {
2155
2370
  var context = context || document
2156
2371
  var finder = Han.find( context )
2157
2372
 
2158
2373
  finder
2159
- .filterOut( 'textarea, code, kbd, samp, pre, hangable', true )
2374
+ .avoid( 'textarea, code, kbd, samp, pre, h-hangable' )
2160
2375
  .replace(
2161
2376
  TYPESET.jinze.hanging,
2162
2377
  function( portion, match ) {
2163
2378
  var elem = $.create( 'h-hangable' )
2164
- var unicode = match[3].charCodeAt( 0 ).toString( 16 )
2165
-
2166
- elem.innerHTML = match[2] + '<h-cs biaodian="' + match[3] + '"><h-inner hidden> </h-inner></h-cs><h-char class="biaodian cjk end" unicode="' + unicode + '">' + match[3] + '</h-char>'
2379
+ elem.innerHTML = match[1] + '<h-cs><h-inner hidden> </h-inner><h-char class="biaodian bd-close bd-end cjk">' + match[2] + '</h-char></h-cs>'
2167
2380
  return portion.index === 0 ? elem : ''
2168
2381
  }
2169
2382
  )
@@ -2175,6 +2388,14 @@ $.extend( Han.fn, {
2175
2388
  hanging: null,
2176
2389
 
2177
2390
  renderHanging: function() {
2391
+ var condClazz = this.condition.classList
2392
+ var isSpaceFontLoaded = detectSpaceFont()
2393
+
2394
+ if ( isSpaceFontLoaded && condClazz.contains( 'no-han-space' )) {
2395
+ condClazz.remove( 'no-han-space' )
2396
+ condClazz.add( 'han-space' )
2397
+ }
2398
+
2178
2399
  this.hanging = Han.renderHanging( this.context )
2179
2400
  return this
2180
2401
  },
@@ -2192,18 +2413,34 @@ Han.renderJiya = function( context ) {
2192
2413
  var finder = Han.find( context )
2193
2414
 
2194
2415
  finder
2195
- .filterOut( 'textarea, code, kbd, samp, pre', true )
2416
+ .avoid( 'textarea, code, kbd, samp, pre, h-char-group' )
2417
+ .replace(
2418
+ // This is a safeguard against hanging rendering
2419
+ new RegExp( '(' + UNICODE.biaodian.end + '+)(' + UNICODE.biaodian.open + '+)', 'g' ),
2420
+ function( portion, match ) {
2421
+ if ( portion.index === 0 ) return portion.isEnd ? match[0] : match[1]
2422
+
2423
+ var elem = $.create( 'h-char-group', 'biaodian cjk portion' )
2424
+ elem.innerHTML = match[2]
2425
+ return elem
2426
+ }
2427
+ )
2428
+ .endAvoid()
2429
+
2430
+ finder
2431
+ .avoid( 'textarea, code, kbd, samp, pre, h-char' )
2196
2432
  .groupify({ biaodian: true })
2197
2433
  .charify({ biaodian: true })
2198
2434
 
2199
2435
  // The reason we're doing this instead of using pseudo elements in CSS
2200
2436
  // is because WebKit has problem rendering pseudo elements containing only
2201
2437
  // space.
2202
- $.qsa( 'h-char.biaodian.open, h-char.biaodian.end', context )
2438
+ $.qsa( 'h-char.biaodian.bd-open, h-char.biaodian.bd-end', context )
2203
2439
  .forEach(function( elem ) {
2440
+ if ( Han.find.matches( elem, 'h-cs *' )) return
2204
2441
  var html = '<h-inner>' + elem.innerHTML + '</h-inner>'
2205
2442
  var hcs = '<h-cs hidden> </h-cs>'
2206
- var isOpen = elem.classList.contains( 'open' )
2443
+ var isOpen = elem.classList.contains( 'bd-open' )
2207
2444
  elem.innerHTML = isOpen ? hcs + html : html + hcs
2208
2445
  })
2209
2446
 
@@ -2228,11 +2465,11 @@ $.extend( Han.fn, {
2228
2465
 
2229
2466
  var mdot
2230
2467
 
2231
- mdot = $.create( 'h-char', 'biaodian cjk middle' )
2468
+ mdot = $.create( 'h-char', 'biaodian cjk bd-middle' )
2232
2469
  mdot.setAttribute( 'unicode', 'b7' )
2233
2470
 
2234
2471
  Han.correctBasicBD = function( context, all ) {
2235
- if ( Han.support.unicoderange && !all ) return
2472
+ if ( Han.support.unicoderange && !all ) return
2236
2473
 
2237
2474
  var context = context || document
2238
2475
  var finder
@@ -2260,33 +2497,35 @@ $.extend( Han.fn, {
2260
2497
  }
2261
2498
  })
2262
2499
 
2263
- var QUERY_RU_W_ANNO = 'h-ru[annotation]'
2500
+ var QUERY_RU_W_ANNO = 'h-ru[annotation]'
2264
2501
  var SELECTOR_TO_IGNORE = 'textarea, code, kbd, samp, pre'
2265
2502
 
2266
- var isCombLigaNormal = (function() {
2267
- var treat = Han.localize.writeOnCanvas( '\u0069\u030D', '"Romanization Sans"' )
2268
- var control = Han.localize.writeOnCanvas( '\uDB80\uDC69', '"Romanization Sans"' )
2269
-
2270
- return Han.localize.compareCanvases( treat, control )
2271
- })()
2272
-
2273
- var aCombLiga = Han.TYPESET[ 'display-as' ][ 'comb-liga-pua' ]
2274
- var aInaccurateChar = Han.TYPESET[ 'inaccurate-char' ]
2503
+ function createCompareFactory( font, treat, control ) {
2504
+ return function() {
2505
+ var a = Han.localize.writeOnCanvas( treat, font )
2506
+ var b = Han.localize.writeOnCanvas( control, font )
2507
+ return Han.localize.compareCanvases( a, b )
2508
+ }
2509
+ }
2275
2510
 
2276
- var charCombLiga = $.create( 'h-char', 'comb-liga' )
2511
+ function isVowelCombLigaNormal() {
2512
+ return createCompareFactory( '"Romanization Sans"', '\u0061\u030D', '\uDB80\uDC61' )
2513
+ }
2277
2514
 
2278
- $.extend( Han, {
2279
- isCombLigaNormal: isCombLigaNormal,
2515
+ function isVowelICombLigaNormal() {
2516
+ return createCompareFactory( '"Romanization Sans"', '\u0069\u030D', '\uDB80\uDC69' )
2517
+ }
2280
2518
 
2281
- substCombLigaWithPUA: function( context ) {
2282
- if ( isCombLigaNormal ) return
2519
+ function isZhuyinCombLigaNormal() {
2520
+ return createCompareFactory( '"Zhuyin Kaiti"', '\u31B4\u0358', '\uDB8C\uDDB4' )
2521
+ }
2283
2522
 
2523
+ function createSubstFactory( regexToSubst ) {
2524
+ return function( context ) {
2284
2525
  var context = context || document
2285
- var finder = Han.find( context )
2286
-
2287
- finder.filterOut( SELECTOR_TO_IGNORE, true )
2526
+ var finder = Han.find( context ).avoid( SELECTOR_TO_IGNORE )
2288
2527
 
2289
- aCombLiga
2528
+ regexToSubst
2290
2529
  .forEach(function( pattern ) {
2291
2530
  finder
2292
2531
  .replace(
@@ -2302,31 +2541,30 @@ $.extend( Han, {
2302
2541
  }
2303
2542
  )
2304
2543
  })
2305
-
2306
- $
2307
- .qsa( QUERY_RU_W_ANNO, context )
2308
- .forEach(function( ru ) {
2309
- var annotation = ru.getAttribute( 'annotation' )
2310
-
2311
- aCombLiga
2312
- // Latin vowels only
2313
- .slice( 0, 5 )
2314
- .forEach(function( pattern ) {
2315
- annotation = annotation.replace(
2316
- new RegExp( pattern[ 0 ], 'ig' ), pattern[ 1 ]
2317
- )
2318
- })
2319
- ru.setAttribute( 'annotation', annotation )
2320
- })
2321
2544
  return finder
2322
- },
2545
+ }
2546
+ }
2547
+
2548
+ var charCombLiga = $.create( 'h-char', 'comb-liga' )
2549
+
2550
+ $.extend( Han, {
2551
+ isVowelCombLigaNormal: isVowelCombLigaNormal(),
2552
+ isVowelICombLigaNormal: isVowelICombLigaNormal(),
2553
+ isZhuyinCombLigaNormal: isZhuyinCombLigaNormal(),
2554
+
2555
+ isCombLigaNormal: isVowelICombLigaNormal()(), // ### Deprecated
2556
+
2557
+ substVowelCombLiga: createSubstFactory( Han.TYPESET[ 'display-as' ][ 'comb-liga-vowel' ] ),
2558
+ substZhuyinCombLiga: createSubstFactory( Han.TYPESET[ 'display-as' ][ 'comb-liga-zhuyin' ] ),
2559
+ substCombLigaWithPUA: createSubstFactory( Han.TYPESET[ 'display-as' ][ 'comb-liga-pua' ] ),
2323
2560
 
2324
2561
  substInaccurateChar: function( context ) {
2325
2562
  var context = context || document
2326
2563
  var finder = Han.find( context )
2327
2564
 
2328
- finder.filterOut( SELECTOR_TO_IGNORE, true )
2329
- aInaccurateChar
2565
+ finder.avoid( SELECTOR_TO_IGNORE )
2566
+
2567
+ Han.TYPESET[ 'inaccurate-char' ]
2330
2568
  .forEach(function( pattern ) {
2331
2569
  finder
2332
2570
  .replace(
@@ -2338,17 +2576,65 @@ $.extend( Han, {
2338
2576
  })
2339
2577
 
2340
2578
  $.extend( Han.fn, {
2341
- 'comb-liga': null,
2342
- 'inaccurate-char': null,
2579
+ 'comb-liga-vowel': null,
2580
+ 'comb-liga-vowel-i': null,
2581
+ 'comb-liga-zhuyin': null,
2582
+ 'inaccurate-char': null,
2583
+
2584
+ substVowelCombLiga: function() {
2585
+ this['comb-liga-vowel'] = Han.substVowelCombLiga( this.context )
2586
+ return this
2587
+ },
2588
+
2589
+ substVowelICombLiga: function() {
2590
+ this['comb-liga-vowel-i'] = Han.substVowelICombLiga( this.context )
2591
+ return this
2592
+ },
2593
+
2594
+ substZhuyinCombLiga: function() {
2595
+ this['comb-liga-zhuyin'] = Han.substZhuyinCombLiga( this.context )
2596
+ return this
2597
+ },
2343
2598
 
2344
2599
  substCombLigaWithPUA: function() {
2345
- this['comb-liga'] = Han.substCombLigaWithPUA( this.context )
2600
+ if ( !Han.isVowelCombLigaNormal()) {
2601
+ this['comb-liga-vowel'] = Han.substVowelCombLiga( this.context )
2602
+ } else if ( !Han.isVowelICombLigaNormal()) {
2603
+ this['comb-liga-vowel-i'] = Han.substVowelICombLiga( this.context )
2604
+ }
2605
+
2606
+ if ( !Han.isZhuyinCombLigaNormal()) {
2607
+ this['comb-liga-zhuyin'] = Han.substZhuyinCombLiga( this.context )
2608
+ }
2609
+ return this
2610
+ },
2611
+
2612
+ revertVowelCombLiga: function() {
2613
+ try {
2614
+ this['comb-liga-vowel'].revert( 'all' )
2615
+ } catch (e) {}
2616
+ return this
2617
+ },
2618
+
2619
+ revertVowelICombLiga: function() {
2620
+ try {
2621
+ this['comb-liga-vowel-i'].revert( 'all' )
2622
+ } catch (e) {}
2623
+ return this
2624
+ },
2625
+
2626
+ revertZhuyinCombLiga: function() {
2627
+ try {
2628
+ this['comb-liga-zhuyin'].revert( 'all' )
2629
+ } catch (e) {}
2346
2630
  return this
2347
2631
  },
2348
2632
 
2349
2633
  revertCombLigaWithPUA: function() {
2350
2634
  try {
2351
- this['comb-liga'].revert( 'all' )
2635
+ this['comb-liga-vowel'].revert( 'all' )
2636
+ this['comb-liga-vowel-i'].revert( 'all' )
2637
+ this['comb-liga-zhuyin'].revert( 'all' )
2352
2638
  } catch (e) {}
2353
2639
  return this
2354
2640
  },
@@ -2382,12 +2668,8 @@ window.addEventListener( 'DOMContentLoaded', function() {
2382
2668
  }
2383
2669
  })
2384
2670
 
2385
- // AMD
2386
- if ( typeof define === 'function' && define.amd ) {
2387
- define(function() { return Han })
2388
-
2389
2671
  // Expose to global namespace
2390
- } else if ( typeof noGlobalNS === 'undefined' || noGlobalNS === false ) {
2672
+ if ( typeof noGlobalNS === 'undefined' || noGlobalNS === false ) {
2391
2673
  window.Han = Han
2392
2674
  }
2393
2675