hanzi-rails 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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