hanzi-rails 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 46b145f590f5c24e09a3f3804b09536a76dabf40
4
- data.tar.gz: b8a2e394816d69b778781ef870c08a5921cd5d8f
3
+ metadata.gz: d6432afbb2c17e412416668ff9b631cf77cb2d4e
4
+ data.tar.gz: 15a05a57491228baa576a452076676a9581bd422
5
5
  SHA512:
6
- metadata.gz: e84c85c534e3a08f113b056ffed19b1957b2836ab479688e83c3b07279102a12c6a2da3a9729ab5c0867906317552a59025dad2c3cfe5c8ef4325af9f4184d84
7
- data.tar.gz: 56ef1f99ab26af5623bc0561fcb1eb1024390e6d30d417a63757014ca18340e524b112121f2243ccc607f643e67f851f86bdbe64e9df75afa0bc378913b14dd4
6
+ metadata.gz: 92a9340628820db6d06759af2cd8bda137f09e429784c3128de8c04ee60c03ed96371fc6a9ae9c3c8f8f5df92a4347166f5e3e9f5dc098ead3322bcb76da2dce
7
+ data.tar.gz: 893ee219a9dff560f6ed8c35725c1f1fb50bde37435ec9a1dd1e312d0431aeae1481604483d1b3224f53df579235610a723c95cf2624daccff8bd1c3cf8c9d39
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  This is a rails gem which provides Sass/JavaScript typesetting framework Han.css.
4
4
 
5
- You can read more about han.css at [here](https://github.com/ethantw/Han/)
5
+ You can read more about han.css at [here](https://github.com/ethantw/Han/) and read manual at [here](https://css.hanzi.co/manual/)
6
6
 
7
7
  han.css is made by Chen Yijun (陳奕鈞,http://yijun.me/).
8
8
 
@@ -30,12 +30,22 @@ The han.css files will be added to the asset pipeline and available for you to u
30
30
  *= require hanzi
31
31
  ```
32
32
 
33
- If you need to use the javascript render function, Add the line to `app/assets/javascripts/application.js`:
33
+ If you need to use the javascript render function, Add these lines to `app/assets/javascripts/application.js`:
34
34
 
35
35
  ```js
36
36
  //= require hanzi
37
+
38
+ Han.init();
37
39
  ```
38
40
 
41
+ And add class `han-init` and correct lang attribute to `html` tag.
42
+
43
+ ```html
44
+ <html lang="zh-Hant" class="han-init">
45
+ ```
46
+
47
+ For more information, please read the [manual](https://css.hanzi.co/manual/js-api#rendering).
48
+
39
49
  ## Contributing
40
50
 
41
51
  1. Fork it ( https://github.com/billy3321/hanzi-rails/fork )
File without changes
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * 漢字標準格式 v3.2.7 | MIT License | css.hanzi.co
2
+ * 漢字標準格式 v3.3.0 | MIT License | css.hanzi.co
3
3
  * Han.css: the CSS typography framework optimised for Hanzi
4
4
  */
5
5
 
@@ -9,7 +9,7 @@ void function( global, factory ) {
9
9
  if ( typeof module === 'object' && typeof module.exports === 'object' ) {
10
10
  module.exports = factory( global, true )
11
11
  // AMD
12
- } else if ( typeof define === 'function' && define.amd ) {
12
+ } else if ( typeof define === 'function' && define.amd ) {
13
13
  define(function() { return factory( global, true ) })
14
14
  // Global namespace
15
15
  } else {
@@ -26,7 +26,7 @@ var root = document.documentElement
26
26
 
27
27
  var body = document.body
28
28
 
29
- var VERSION = '3.2.7'
29
+ var VERSION = '3.3.0'
30
30
 
31
31
  var ROUTINE = [
32
32
  // Initialise the condition with feature-detecting
@@ -39,15 +39,15 @@ var ROUTINE = [
39
39
 
40
40
  // Handle Biaodian
41
41
  /* 'jinzify', */
42
- 'renderHanging',
43
42
  'renderJiya',
43
+ 'renderHanging',
44
+
45
+ // Address Biaodian correction
46
+ 'correctBiaodian',
44
47
 
45
48
  // Address Hanzi and Western script mixed spacing
46
49
  'renderHWS',
47
50
 
48
- // Address Basic Biaodian correction in Firefox
49
- 'correctBasicBD',
50
-
51
51
  // Address presentational correction to combining ligatures
52
52
  'substCombLigaWithPUA'
53
53
 
@@ -101,17 +101,23 @@ Han.fn = Han.prototype = {
101
101
  // the instance or in the prototype chain.
102
102
  render: function( routine ) {
103
103
  var it = this
104
- var routine = Array.isArray( routine ) ? routine : this.routine
104
+ var routine = Array.isArray( routine )
105
+ ? routine
106
+ : this.routine
105
107
 
106
108
  routine
107
109
  .forEach(function( method ) {
108
- try {
109
- if ( typeof method === 'string' ) {
110
- it[ method ]()
111
- } else if ( Array.isArray( method )) {
112
- it[ method.shift() ].apply( it, method )
113
- }
114
- } catch ( e ) {}
110
+ if (
111
+ typeof method === 'string' &&
112
+ typeof it[ method ] === 'function'
113
+ ) {
114
+ it[ method ]()
115
+ } else if (
116
+ Array.isArray( method ) &&
117
+ typeof it[ method[0] ] === 'function'
118
+ ) {
119
+ it[ method.shift() ].apply( it, method )
120
+ }
115
121
  })
116
122
  return this
117
123
  }
@@ -325,31 +331,31 @@ var TYPESET = (function() {
325
331
  var rPtMid = UNICODE.punct.middle
326
332
  var rPtSing = UNICODE.punct.sing
327
333
  var rPt = rPtOpen + '|' + rPtEnd + '|' + rPtMid
328
-
329
- var rBdOpen = UNICODE.biaodian.open
330
- var rBdClose = UNICODE.biaodian.close
331
- var rBdEnd = UNICODE.biaodian.end
332
- var rBdMid = UNICODE.biaodian.middle
333
- var rBdLiga = UNICODE.biaodian.liga + '{2}'
334
- var rBd = rBdOpen + '|' + rBdEnd + '|' + rBdMid
335
-
334
+
335
+ var rBDOpen = UNICODE.biaodian.open
336
+ var rBDClose = UNICODE.biaodian.close
337
+ var rBDEnd = UNICODE.biaodian.end
338
+ var rBDMid = UNICODE.biaodian.middle
339
+ var rBDLiga = UNICODE.biaodian.liga + '{2}'
340
+ var rBD = rBDOpen + '|' + rBDEnd + '|' + rBDMid
341
+
336
342
  var rKana = UNICODE.kana.base + UNICODE.kana.combine + '?'
337
343
  var rKanaS = UNICODE.kana.small + UNICODE.kana.combine + '?'
338
344
  var rKanaH = UNICODE.kana.half
339
345
  var rEon = UNICODE.eonmun.base + '|' + UNICODE.eonmun.letter
340
346
  var rEonH = UNICODE.eonmun.half
341
-
347
+
342
348
  var rHan = UNICODE.hanzi.base + '|' + UNICODE.hanzi.desc + '|' + UNICODE.hanzi.radical + '|' + rKana
343
-
349
+
344
350
  var rCbn = UNICODE.ellinika.combine
345
351
  var rLatn = UNICODE.latin.base + rCbn + '*'
346
352
  var rGk = UNICODE.ellinika.base + rCbn + '*'
347
-
353
+
348
354
  var rCyCbn = UNICODE.kirillica.combine
349
355
  var rCy = UNICODE.kirillica.base + rCyCbn + '*'
350
-
356
+
351
357
  var rAlph = rLatn + '|' + rGk + '|' + rCy
352
-
358
+
353
359
  // For words like `it's`, `Jones’s` or `'99`
354
360
  var rApo = '[\u0027\u2019]'
355
361
  var rChar = rHan + '|(?:' + rAlph + '|' + rApo + ')+'
@@ -371,29 +377,29 @@ var TYPESET = (function() {
371
377
  },
372
378
 
373
379
  biaodian: {
374
- all: new RegExp( '(' + rBd + ')', 'g' ),
375
- open: new RegExp( '(' + rBdOpen + ')', 'g' ),
376
- close: new RegExp( '(' + rBdClose + ')', 'g' ),
377
- end: new RegExp( '(' + rBdEnd + ')', 'g' ),
378
- liga: new RegExp( '(' + rBdLiga + ')', 'g' )
380
+ all: new RegExp( '(' + rBD + ')', 'g' ),
381
+ open: new RegExp( '(' + rBDOpen + ')', 'g' ),
382
+ close: new RegExp( '(' + rBDClose + ')', 'g' ),
383
+ end: new RegExp( '(' + rBDEnd + ')', 'g' ),
384
+ liga: new RegExp( '(' + rBDLiga + ')', 'g' )
379
385
  },
380
386
 
381
- hanzi: new RegExp( '(' + rHan + ')', 'g' ),
387
+ hanzi: new RegExp( '(' + rHan + ')', 'g' ),
382
388
 
383
- latin: new RegExp( '(' + rLatn + ')', 'ig' ),
384
- ellinika: new RegExp( '(' + rGk + ')', 'ig' ),
385
- kirillica: new RegExp( '(' + rCy + ')', 'ig' ),
389
+ latin: new RegExp( '(' + rLatn + ')', 'ig' ),
390
+ ellinika: new RegExp( '(' + rGk + ')', 'ig' ),
391
+ kirillica: new RegExp( '(' + rCy + ')', 'ig' ),
386
392
 
387
- kana: new RegExp( '(' + rKana + '|' + rKanaS + '|' + rKanaH + ')', 'g' ),
388
- eonmun: new RegExp( '(' + rEon + '|' + rEonH + ')', 'g' )
393
+ kana: new RegExp( '(' + rKana + '|' + rKanaS + '|' + rKanaH + ')', 'g' ),
394
+ eonmun: new RegExp( '(' + rEon + '|' + rEonH + ')', 'g' )
389
395
  },
390
396
 
391
397
  /* Word-level selectors (詞級選擇器)
392
398
  */
393
399
  group: {
394
400
  biaodian: [
395
- new RegExp( '((' + rBd + '){2,})', 'g' ),
396
- new RegExp( '(' + rBdLiga + rBdOpen + ')', 'g' )
401
+ new RegExp( '((' + rBD + '){2,})', 'g' ),
402
+ new RegExp( '(' + rBDLiga + rBDOpen + ')', 'g' )
397
403
  ],
398
404
  punct: null,
399
405
  hanzi: new RegExp( '(' + rHan + ')+', 'g' ),
@@ -405,11 +411,11 @@ var TYPESET = (function() {
405
411
  /* Punctuation Rules (禁則)
406
412
  */
407
413
  jinze: {
408
- hanging: new RegExp( '(' + rBdClose + '*|[…⋯]*)([、,。.])(?!' + rBdEnd + ')', 'ig' ),
409
- touwei: new RegExp( '(' + rBdOpen + '+)(' + rChar + ')(' + rBdEnd + '+)', 'ig' ),
410
- tou: new RegExp( '(' + rBdOpen + '+)(' + rChar + ')', 'ig' ),
411
- wei: new RegExp( '(' + rChar + ')(' + rBdEnd + '+)', 'ig' ),
412
- middle: new RegExp( '(' + rChar + ')(' + rBdMid + ')(' + rChar + ')', 'ig' )
414
+ hanging: new RegExp( rWhite + '*([、,。.])(?!' + rBDEnd + ')', 'ig' ),
415
+ touwei: new RegExp( '(' + rBDOpen + '+)(' + rChar + ')(' + rBDEnd + '+)', 'ig' ),
416
+ tou: new RegExp( '(' + rBDOpen + '+)(' + rChar + ')', 'ig' ),
417
+ wei: new RegExp( '(' + rChar + ')(' + rBDEnd + '+)', 'ig' ),
418
+ middle: new RegExp( '(' + rChar + ')(' + rBDMid + ')(' + rChar + ')', 'ig' )
413
419
  },
414
420
 
415
421
  zhuyin: {
@@ -507,71 +513,119 @@ Han.TYPESET.char.greek = Han.TYPESET.char.ellinika
507
513
  Han.TYPESET.char.cyrillic = Han.TYPESET.char.kirillica
508
514
  Han.TYPESET.char.hangul = Han.TYPESET.char.eonmun
509
515
 
516
+ Han.TYPESET.group.hangul = Han.TYPESET.group.eonmun
517
+ Han.TYPESET.group.cjk = Han.TYPESET.group.hanzi
518
+
510
519
  var $ = {
511
- // Simplified query selectors which return the node list
512
- // in an array
513
- id: function( selector, context ) {
514
- return ( context || document ).getElementById( selector )
520
+ /**
521
+ * Query selectors which return arrays of the resulted
522
+ * node lists.
523
+ */
524
+ id: function( selector, $context ) {
525
+ return ( $context || document ).getElementById( selector )
515
526
  },
516
527
 
517
- tag: function( selector, context ) {
528
+ tag: function( selector, $context ) {
518
529
  return this.makeArray(
519
- ( context || document ).getElementsByTagName( selector )
530
+ ( $context || document ).getElementsByTagName( selector )
520
531
  )
521
532
  },
522
533
 
523
- qsa: function( selector, context ) {
534
+ qs: function( selector, $context ) {
535
+ return ( $context || document ).querySelector( selector )
536
+ },
537
+
538
+ qsa: function( selector, $context ) {
524
539
  return this.makeArray(
525
- ( context || document ).querySelectorAll( selector )
540
+ ( $context || document ).querySelectorAll( selector )
526
541
  )
527
542
  },
528
543
 
529
- // Create a document fragment, a text node with text
530
- // or an element with/without classes
531
- create: function( elem, clazz ) {
532
- var elem = '!' === elem ?
533
- document.createDocumentFragment() :
534
- '' === elem ?
535
- document.createTextNode( clazz || '' ) :
536
- document.createElement( elem )
544
+ parent: function( $node, selector ) {
545
+ return selector
546
+ ? (function() {
547
+ if ( typeof $.matches !== 'function' ) return
548
+
549
+ while (!$.matches( $node, selector )) {
550
+ if (
551
+ !$node ||
552
+ $node === document.documentElement
553
+ ) {
554
+ $node = undefined
555
+ break
556
+ }
557
+ $node = $node.parentNode
558
+ }
559
+ return $node
560
+ })()
561
+ : $node
562
+ ? $node.parentNode : undefined
563
+ },
564
+
565
+ /**
566
+ * Create a document fragment, a text node with text
567
+ * or an element with/without classes.
568
+ */
569
+ create: function( name, clazz ) {
570
+ var $elmt = '!' === name
571
+ ? document.createDocumentFragment()
572
+ : '' === name
573
+ ? document.createTextNode( clazz || '' )
574
+ : document.createElement( name )
537
575
 
538
576
  try {
539
577
  if ( clazz ) {
540
- elem.className = clazz
578
+ $elmt.className = clazz
541
579
  }
542
580
  } catch (e) {}
543
581
 
544
- return elem
582
+ return $elmt
545
583
  },
546
584
 
547
- // Clone a node (text, element or fragment) deeply or
548
- // childlessly
549
- clone: function( node, deep ) {
550
- return node.cloneNode( typeof deep === 'boolean' ? deep : true )
585
+ /**
586
+ * Clone a DOM node (text, element or fragment) deeply
587
+ * or childlessly.
588
+ */
589
+ clone: function( $node, deep ) {
590
+ return $node.cloneNode(
591
+ typeof deep === 'boolean'
592
+ ? deep
593
+ : true
594
+ )
551
595
  },
552
596
 
553
- // Remove a node (text, element or fragment)
554
- remove: function( node ) {
555
- return node.parentNode.removeChild( node )
597
+ /**
598
+ * Remove a node (text, element or fragment).
599
+ */
600
+ remove: function( $node ) {
601
+ return $node.parentNode.removeChild( $node )
556
602
  },
557
603
 
558
- // Set attributes all in once with an object
604
+ /**
605
+ * Set attributes all in once with an object.
606
+ */
559
607
  setAttr: function( target, attr ) {
560
- if ( typeof attr !== 'object' ) return
608
+ if ( typeof attr !== 'object' ) return
561
609
  var len = attr.length
562
610
 
563
- // Native NamedNodeMap
564
- if ( typeof attr[ 0 ] === 'object' && 'name' in attr[ 0 ] ) {
611
+ // Native `NamedNodeMap``:
612
+ if (
613
+ typeof attr[0] === 'object' &&
614
+ 'name' in attr[0]
615
+ ) {
565
616
  for ( var i = 0; i < len; i++ ) {
566
617
  if ( attr[ i ].value !== undefined ) {
567
618
  target.setAttribute( attr[ i ].name, attr[ i ].value )
568
619
  }
569
620
  }
570
621
 
571
- // Plain object
622
+ // Plain object:
572
623
  } else {
573
624
  for ( var name in attr ) {
574
- if ( attr.hasOwnProperty( name ) && attr[ name ] !== undefined ) {
625
+ if (
626
+ attr.hasOwnProperty( name ) &&
627
+ attr[ name ] !== undefined
628
+ ) {
575
629
  target.setAttribute( name, attr[ name ] )
576
630
  }
577
631
  }
@@ -579,29 +633,47 @@ var $ = {
579
633
  return target
580
634
  },
581
635
 
582
- // Return if the current node should be ignored,
583
- // `<wbr>` or comments
584
- isIgnorable: function( node ) {
585
- return node.nodeName === 'WBR' || node.nodeType === Node.COMMENT_NODE
636
+ /**
637
+ * Indicate whether or not the given node is an
638
+ * element.
639
+ */
640
+ isElmt: function( $node ) {
641
+ return $node && $node.nodeType === Node.ELEMENT_NODE
586
642
  },
587
643
 
588
- // Convert array-like objects into real arrays
589
- // for the native prototype methods
590
- makeArray: function( obj ) {
591
- return Array.prototype.slice.call( obj )
644
+ /**
645
+ * Indicate whether or not the given node should
646
+ * be ignored (`<wbr>` or comments).
647
+ */
648
+ isIgnorable: function( $node ) {
649
+ if ( !$node ) return false
650
+
651
+ return (
652
+ $node.nodeName === 'WBR' ||
653
+ $node.nodeType === Node.COMMENT_NODE
654
+ )
655
+ },
656
+
657
+ /**
658
+ * Convert array-like objects into real arrays.
659
+ */
660
+ makeArray: function( object ) {
661
+ return Array.prototype.slice.call( object )
592
662
  },
593
663
 
594
- // Extend target with an object
664
+ /**
665
+ * Extend target with an object.
666
+ */
595
667
  extend: function( target, object ) {
596
- var isExtensible = typeof target === 'object' ||
597
- typeof target === 'function' ||
668
+ if ((
669
+ typeof target === 'object' ||
670
+ typeof target === 'function' ) &&
598
671
  typeof object === 'object'
599
-
600
- if ( !isExtensible ) return
601
-
602
- for ( var name in object ) {
603
- if ( object.hasOwnProperty( name )) {
604
- target[ name ] = object[ name ]
672
+ ) {
673
+ for ( var name in object ) {
674
+ if (object.hasOwnProperty( name )) {
675
+ target[ name ] = object[ name ]
676
+ }
605
677
  }
606
678
  }
607
679
  return target
@@ -628,9 +700,9 @@ var document = global.document || undefined
628
700
  function matches( node, selector, bypassNodeType39 ) {
629
701
  var Efn = Element.prototype
630
702
  var matches = Efn.matches || Efn.mozMatchesSelector || Efn.msMatchesSelector || Efn.webkitMatchesSelector
631
-
703
+
632
704
  if ( node instanceof Element ) {
633
- return matches.call( node, selector )
705
+ return matches.call( node, selector )
634
706
  } else if ( bypassNodeType39 ) {
635
707
  if ( /^[39]$/.test( node.nodeType )) return true
636
708
  }
@@ -672,7 +744,7 @@ Fibre.fn = Fibre.prototype = {
672
744
  }
673
745
 
674
746
  if ( !context ) {
675
- throw new Error( 'A context is required for Fibre to initialise.' )
747
+ throw new Error( 'A context is required for Fibre to initialise.' )
676
748
  } else if ( context instanceof Node ) {
677
749
  if ( context instanceof Document ) this.context = context.body || context
678
750
  else this.context = context
@@ -748,23 +820,23 @@ Fibre.fn = Fibre.prototype = {
748
820
  replace: function( regexp, newSubStr ) {
749
821
  var it = this
750
822
  it.finder.push(Finder( it.context, {
751
- find: regexp,
823
+ find: regexp,
752
824
  replace: newSubStr,
753
825
  filterElements: function( currentNode ) {
754
826
  return it.filterFn( currentNode )
755
- },
827
+ },
756
828
  forceContext: function( currentNode ) {
757
829
  return it.boundaryFn( currentNode )
758
830
  },
759
831
  portionMode: it.portionMode
760
832
  }))
761
- return it
833
+ return it
762
834
  },
763
835
 
764
836
  wrap: function( regexp, strElemName ) {
765
837
  var it = this
766
838
  it.finder.push(Finder( it.context, {
767
- find: regexp,
839
+ find: regexp,
768
840
  wrap: strElemName,
769
841
  filterElements: function( currentNode ) {
770
842
  return it.filterFn( currentNode )
@@ -778,7 +850,7 @@ Fibre.fn = Fibre.prototype = {
778
850
  },
779
851
 
780
852
  revert: function( level ) {
781
- var max = this.finder.length
853
+ var max = this.finder.length
782
854
  var level = Number( level ) || ( level === 0 ? Number(0) :
783
855
  ( level === 'all' ? max : 1 ))
784
856
 
@@ -862,7 +934,7 @@ return Fibre
862
934
  m.index += m[0].indexOf(cg)
863
935
  m[0] = cg
864
936
  }
865
-
937
+
866
938
  m.endIndex = m.index + m[0].length
867
939
  m.startIndex = m.index
868
940
  m.index = mi
@@ -876,9 +948,9 @@ return Fibre
876
948
  return true
877
949
  }
878
950
 
879
- /**
951
+ /**
880
952
  * findAndReplaceDOMText
881
- *
953
+ *
882
954
  * Locates matches and replaces with replacementNode
883
955
  *
884
956
  * @param {Node} node Element or Text node to search within
@@ -1011,7 +1083,7 @@ return Fibre
1011
1083
  if (!match[0]) {
1012
1084
  throw new Error('findAndReplaceDOMText cannot handle zero-length matches')
1013
1085
  }
1014
-
1086
+
1015
1087
  match.endIndex = characterOffset + match.index + match[0].length
1016
1088
  match.startIndex = characterOffset + match.index
1017
1089
  match.index = matchIndex
@@ -1072,10 +1144,10 @@ return Fibre
1072
1144
  } while (node = node.nextSibling)
1073
1145
  return txt
1074
1146
  }
1075
-
1147
+
1076
1148
  },
1077
1149
 
1078
- /**
1150
+ /**
1079
1151
  * Steps through the target node, looking for matches, and
1080
1152
  * calling replaceFn when a match is found.
1081
1153
  */
@@ -1141,7 +1213,7 @@ return Fibre
1141
1213
 
1142
1214
  curNode = this.replaceMatch(match, startPortion, innerPortions, endPortion)
1143
1215
  // processMatches has to return the node that replaced the endNode
1144
- // and then we step back so we can continue from the end of the
1216
+ // and then we step back so we can continue from the end of the
1145
1217
  // match:
1146
1218
 
1147
1219
  atIndex -= (endPortion.node.data.length - endPortion.endIndexInNode)
@@ -1363,37 +1435,82 @@ return Fibre
1363
1435
 
1364
1436
  );
1365
1437
 
1366
- function createBdGroup( portion, match ) {
1367
- var elem = $.create( 'h-char-group', 'biaodian cjk' )
1438
+ var isNodeNormalizeNormal = (function() {
1439
+ //// Disabled `Node.normalize()` for temp due to
1440
+ //// issue below in IE11.
1441
+ //// See: http://stackoverflow.com/questions/22337498/why-does-ie11-handle-node-normalize-incorrectly-for-the-minus-symbol
1442
+ var div = $.create( 'div' )
1368
1443
 
1369
- if ( portion.index === 0 && portion.isEnd ) {
1370
- elem.innerHTML = match[0]
1371
- } else {
1372
- elem.innerHTML = portion.text
1373
- elem.classList.add( 'portion' )
1444
+ div.appendChild($.create( '', '0-' ))
1445
+ div.appendChild($.create( '', '2' ))
1446
+ div.normalize()
1374
1447
 
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
1448
+ return div.firstChild.length !== 2
1449
+ })()
1450
+
1451
+ function getFuncOrElmt( obj ) {
1452
+ return (
1453
+ typeof obj === 'function' ||
1454
+ obj instanceof Element
1455
+ )
1456
+ ? obj
1457
+ : undefined
1382
1458
  }
1383
1459
 
1384
- function createBdChar( char ) {
1385
- var div = $.create( 'div' )
1460
+ function createBDGroup( portion ) {
1461
+ var clazz = portion.index === 0 && portion.isEnd
1462
+ ? 'biaodian cjk'
1463
+ : 'biaodian cjk portion ' + (
1464
+ portion.index === 0
1465
+ ? 'is-first'
1466
+ : portion.isEnd
1467
+ ? 'is-end'
1468
+ : 'is-inner'
1469
+ )
1470
+
1471
+ var $elmt = $.create( 'h-char-group', clazz )
1472
+ $elmt.innerHTML = portion.text
1473
+ return $elmt
1474
+ }
1475
+
1476
+ function createBDChar( char ) {
1477
+ var div = $.create( 'div' )
1386
1478
  var unicode = char.charCodeAt( 0 ).toString( 16 )
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' : '' )
1391
1479
 
1392
- div.innerHTML = '<h-char unicode="' + unicode + '" class="' + clazz + '">' + char + '</h-char>'
1480
+ div.innerHTML = (
1481
+ '<h-char unicode="' + unicode +
1482
+ '" class="biaodian cjk ' + getBDType( char ) +
1483
+ '">' + char + '</h-char>'
1484
+ )
1393
1485
  return div.firstChild
1394
1486
  }
1395
1487
 
1488
+ function getBDType( char ) {
1489
+ return char.match( TYPESET.char.biaodian.open )
1490
+ ? 'bd-open'
1491
+ : char.match( TYPESET.char.biaodian.close )
1492
+ ? 'bd-close bd-end'
1493
+ : char.match( TYPESET.char.biaodian.end )
1494
+ ? (
1495
+ /(?:\u3001|\u3002|\uff0c)/i.test( char )
1496
+ ? 'bd-end bd-cop'
1497
+ : 'bd-end'
1498
+ )
1499
+ : char.match(new RegExp( UNICODE.biaodian.liga ))
1500
+ ? 'bd-liga'
1501
+ : char.match(new RegExp( UNICODE.biaodian.middle ))
1502
+ ? 'bd-middle'
1503
+ : ''
1504
+ }
1505
+
1396
1506
  $.extend( Fibre.fn, {
1507
+ normalize: function() {
1508
+ if ( isNodeNormalizeNormal ) {
1509
+ this.context.normalize()
1510
+ }
1511
+ return this
1512
+ },
1513
+
1397
1514
  // Force punctuation & biaodian typesetting rules to be applied.
1398
1515
  jinzify: function( selector ) {
1399
1516
  return (
@@ -1449,33 +1566,34 @@ $.extend( Fibre.fn, {
1449
1566
  western: false // Includes Latin, Greek and Cyrillic
1450
1567
  }, option || {})
1451
1568
 
1452
- this.avoid( 'h-hangable, h-char-group, h-word' )
1569
+ this.avoid( 'h-word, h-char-group' )
1453
1570
 
1454
1571
  if ( option.biaodian ) {
1455
1572
  this.replace(
1456
- TYPESET.group.biaodian[ 0 ], createBdGroup
1573
+ TYPESET.group.biaodian[0], createBDGroup
1457
1574
  ).replace(
1458
- TYPESET.group.biaodian[ 1 ], createBdGroup
1575
+ TYPESET.group.biaodian[1], createBDGroup
1459
1576
  )
1460
1577
  }
1578
+
1461
1579
  if ( option.hanzi || option.cjk ) {
1462
1580
  this.wrap(
1463
- TYPESET.group.hanzi, $.clone( $.create( 'h-char-group', 'hanzi cjk' ))
1581
+ TYPESET.group.hanzi, $.clone($.create( 'h-char-group', 'hanzi cjk' ))
1464
1582
  )
1465
1583
  }
1466
1584
  if ( option.western ) {
1467
1585
  this.wrap(
1468
- TYPESET.group.western, $.clone( $.create( 'h-word', 'western' ))
1586
+ TYPESET.group.western, $.clone($.create( 'h-word', 'western' ))
1469
1587
  )
1470
1588
  }
1471
1589
  if ( option.kana ) {
1472
1590
  this.wrap(
1473
- TYPESET.group.kana, $.clone( $.create( 'h-char-group', 'kana' ))
1591
+ TYPESET.group.kana, $.clone($.create( 'h-char-group', 'kana' ))
1474
1592
  )
1475
1593
  }
1476
1594
  if ( option.eonmun || option.hangul ) {
1477
1595
  this.wrap(
1478
- TYPESET.group.eonmun, $.clone( $.create( 'h-word', 'eonmun hangul' ))
1596
+ TYPESET.group.eonmun, $.clone($.create( 'h-word', 'eonmun hangul' ))
1479
1597
  )
1480
1598
  }
1481
1599
 
@@ -1485,6 +1603,7 @@ $.extend( Fibre.fn, {
1485
1603
 
1486
1604
  charify: function( option ) {
1487
1605
  var option = $.extend({
1606
+ avoid: true,
1488
1607
  biaodian: false,
1489
1608
  punct: false,
1490
1609
  hanzi: false, // Includes Kana
@@ -1495,50 +1614,77 @@ $.extend( Fibre.fn, {
1495
1614
  eonmun: false
1496
1615
  }, option || {})
1497
1616
 
1498
- this.avoid( 'h-char' )
1617
+ if ( option.avoid ) {
1618
+ this.avoid( 'h-char' )
1619
+ }
1499
1620
 
1500
1621
  if ( option.biaodian ) {
1501
1622
  this.replace(
1502
1623
  TYPESET.char.biaodian.all,
1503
- function( portion, match ) { return createBdChar( match[0] ) }
1624
+ getFuncOrElmt( option.biaodian )
1625
+ ||
1626
+ function( portion ) { return createBDChar( portion.text ) }
1504
1627
  ).replace(
1505
1628
  TYPESET.char.biaodian.liga,
1506
- function( portion, match ) { return createBdChar( match[0] ) }
1629
+ getFuncOrElmt( option.biaodian )
1630
+ ||
1631
+ function( portion ) { return createBDChar( portion.text ) }
1507
1632
  )
1508
1633
  }
1509
1634
  if ( option.hanzi || option.cjk ) {
1510
1635
  this.wrap(
1511
- TYPESET.char.hanzi, $.clone( $.create( 'h-char', 'hanzi cjk' ))
1636
+ TYPESET.char.hanzi,
1637
+ getFuncOrElmt( option.hanzi || option.cjk )
1638
+ ||
1639
+ $.clone($.create( 'h-char', 'hanzi cjk' ))
1512
1640
  )
1513
1641
  }
1514
1642
  if ( option.punct ) {
1515
1643
  this.wrap(
1516
- TYPESET.char.punct.all, $.clone( $.create( 'h-char', 'punct' ))
1644
+ TYPESET.char.punct.all,
1645
+ getFuncOrElmt( option.punct )
1646
+ ||
1647
+ $.clone($.create( 'h-char', 'punct' ))
1517
1648
  )
1518
1649
  }
1519
1650
  if ( option.latin ) {
1520
1651
  this.wrap(
1521
- TYPESET.char.latin, $.clone( $.create( 'h-char', 'alphabet latin' ))
1652
+ TYPESET.char.latin,
1653
+ getFuncOrElmt( option.latin )
1654
+ ||
1655
+ $.clone($.create( 'h-char', 'alphabet latin' ))
1522
1656
  )
1523
1657
  }
1524
1658
  if ( option.ellinika || option.greek ) {
1525
1659
  this.wrap(
1526
- TYPESET.char.ellinika, $.clone( $.create( 'h-char', 'alphabet ellinika greek' ))
1660
+ TYPESET.char.ellinika,
1661
+ getFuncOrElmt( option.ellinika || option.greek )
1662
+ ||
1663
+ $.clone($.create( 'h-char', 'alphabet ellinika greek' ))
1527
1664
  )
1528
1665
  }
1529
1666
  if ( option.kirillica || option.cyrillic ) {
1530
1667
  this.wrap(
1531
- TYPESET.char.kirillica, $.clone( $.create( 'h-char', 'alphabet kirillica cyrillic' ))
1668
+ TYPESET.char.kirillica,
1669
+ getFuncOrElmt( option.kirillica || option.cyrillic )
1670
+ ||
1671
+ $.clone($.create( 'h-char', 'alphabet kirillica cyrillic' ))
1532
1672
  )
1533
1673
  }
1534
1674
  if ( option.kana ) {
1535
1675
  this.wrap(
1536
- TYPESET.char.kana, $.clone( $.create( 'h-char', 'kana' ))
1676
+ TYPESET.char.kana,
1677
+ getFuncOrElmt( option.kana )
1678
+ ||
1679
+ $.clone($.create( 'h-char', 'kana' ))
1537
1680
  )
1538
1681
  }
1539
1682
  if ( option.eonmun || option.hangul ) {
1540
1683
  this.wrap(
1541
- TYPESET.char.eonmun, $.clone( $.create( 'h-char', 'eonmun hangul' ))
1684
+ TYPESET.char.eonmun,
1685
+ getFuncOrElmt( option.eonmun || option.hangul )
1686
+ ||
1687
+ $.clone($.create( 'h-char', 'eonmun hangul' ))
1542
1688
  )
1543
1689
  }
1544
1690
 
@@ -1547,7 +1693,14 @@ $.extend( Fibre.fn, {
1547
1693
  }
1548
1694
  })
1549
1695
 
1550
- Han.find = Fibre
1696
+ $.extend( Han, {
1697
+ isNodeNormalizeNormal: isNodeNormalizeNormal,
1698
+ find: Fibre,
1699
+ createBDGroup: createBDGroup,
1700
+ createBDChar: createBDChar
1701
+ })
1702
+
1703
+ $.matches = Han.find.matches
1551
1704
 
1552
1705
  void [
1553
1706
  'setMode',
@@ -2021,7 +2174,7 @@ function createNormalRu( $rb, $rt, attr ) {
2021
2174
  if ( Array.isArray( $rb )) {
2022
2175
  $ru.innerHTML = $rb.map(function( rb ) {
2023
2176
  if ( typeof rb === 'undefined' ) return ''
2024
- return rb.outerHTML
2177
+ return rb.outerHTML
2025
2178
  }).join('') + $rt.outerHTML
2026
2179
  } else {
2027
2180
  $ru.appendChild( $.clone( $rb ))
@@ -2131,32 +2284,34 @@ $.extend( Locale, {
2131
2284
  this.renderEm( context )
2132
2285
  },
2133
2286
 
2134
- // Traverse target elements (those with text-decoration-
2135
- // line) to see if we should address spacing in
2136
- // between for semantic presentation.
2287
+ // Traverse all target elements and address
2288
+ // presentational corrections if any two of
2289
+ // them are adjacent to each other.
2137
2290
  renderDecoLine: function( context, target ) {
2138
- var target = target || 'u, ins'
2139
- var $target = $.qsa( target, context )
2140
- var rTarget = new RegExp( '^(' + target.replace(/\,\s?/g, '|') + ')$', 'ig' )
2141
-
2142
- $target
2143
- .forEach(function( elem ) {
2144
- var next
2145
-
2146
- // Ignore all `<wbr>` and comments in between
2147
- do {
2148
- next = ( next || elem ).nextSibling
2149
- if ( !next ) return
2150
- } while ( $.isIgnorable( next ))
2151
-
2152
- if ( next.nodeName.match( rTarget )) {
2153
- next.classList.add( 'adjacent' )
2154
- }
2155
- })
2291
+ var $$target = $.qsa( target || 'u, ins', context )
2292
+ var i = $$target.length
2293
+
2294
+ traverse: while ( i-- ) {
2295
+ var $this = $$target[ i ]
2296
+ var $prev = null
2297
+
2298
+ // Ignore all `<wbr>` and comments in between,
2299
+ // and add class `.adjacent` once two targets
2300
+ // are next to each other.
2301
+ ignore: do {
2302
+ $prev = ( $prev || $this ).previousSibling
2303
+
2304
+ if ( !$prev ) {
2305
+ continue traverse
2306
+ } else if ( $$target[ i-1 ] === $prev ) {
2307
+ $this.classList.add( 'adjacent' )
2308
+ }
2309
+ } while ( $.isIgnorable( $prev ))
2310
+ }
2156
2311
  },
2157
2312
 
2158
- // Traverse target elements to render Hanzi emphasis marks
2159
- // and skip that in punctuation
2313
+ // Traverse all target elements to render
2314
+ // emphasis marks.
2160
2315
  renderEm: function( context, target ) {
2161
2316
  var method = target ? 'qsa' : 'tag'
2162
2317
  var target = target || 'em'
@@ -2168,13 +2323,13 @@ $.extend( Locale, {
2168
2323
 
2169
2324
  if ( Locale.support.textemphasis ) {
2170
2325
  $elem
2171
- .avoid( 'rt, h-char, h-char-group' )
2326
+ .avoid( 'rt, h-char' )
2172
2327
  .charify({ biaodian: true, punct: true })
2173
2328
  } else {
2174
2329
  $elem
2175
2330
  .avoid( 'rt, h-char, h-char-group' )
2176
2331
  .jinzify()
2177
- .groupify({ western: true, biaodian: true })
2332
+ .groupify({ western: true })
2178
2333
  .charify({
2179
2334
  hanzi: true,
2180
2335
  biaodian: true,
@@ -2229,101 +2384,160 @@ $.extend( Han.support, {
2229
2384
  // 'fangsong-gb': Han.detectFont( '"Han Fangsong GB"' )
2230
2385
  })
2231
2386
 
2232
- var QUERY_HWS_AS_FIRST_CHILD = '* > h-hws:first-child, * > wbr:first-child + h-hws, wbr:first-child + wbr + h-hws'
2387
+ Han.correctBiaodian = function( context ) {
2388
+ var context = context || document
2389
+ var finder = Han.find( context )
2233
2390
 
2234
- //// Disabled `Node.normalize()` for temp due to
2235
- //// issue below in IE11.
2236
- //// See: http://stackoverflow.com/questions/22337498/why-does-ie11-handle-node-normalize-incorrectly-for-the-minus-symbol
2237
- var isNodeNormalizeNormal = (function() {
2238
- var div = $.create( 'div' )
2391
+ finder
2392
+ .avoid( 'h-char' )
2393
+ .replace( /([‘“])/g, function( portion ) {
2394
+ var $char = Han.createBDChar( portion.text )
2395
+ $char.classList.add( 'bd-open', 'punct' )
2396
+ return $char
2397
+ })
2398
+ .replace( /([’”])/g, function( portion ) {
2399
+ var $char = Han.createBDChar( portion.text )
2400
+ $char.classList.add( 'bd-close', 'bd-end', 'punct' )
2401
+ return $char
2402
+ })
2239
2403
 
2240
- div.appendChild( $.create( '', '0-' ))
2241
- div.appendChild( $.create( '', '2' ))
2242
- div.normalize()
2404
+ return Han.support.unicoderange
2405
+ ? finder
2406
+ : finder.charify({ biaodian: true })
2407
+ }
2243
2408
 
2244
- return div.firstChild.length !== 2
2245
- })()
2409
+ Han.correctBasicBD = Han.correctBiaodian
2410
+ Han.correctBD = Han.correctBiaodian
2246
2411
 
2247
- var hws = $.create( 'h-hws' )
2248
- hws.setAttribute( 'hidden', '' )
2249
- hws.innerHTML = ' '
2412
+ $.extend( Han.fn, {
2413
+ biaodian: null,
2250
2414
 
2251
- $.extend( Han, {
2252
- isNodeNormalizeNormal: isNodeNormalizeNormal,
2415
+ correctBiaodian: function() {
2416
+ this.biaodian = Han.correctBiaodian( this.context )
2417
+ return this
2418
+ },
2253
2419
 
2254
- renderHWS: function( context, strict ) {
2255
- var context = context || document
2256
- var mode = strict ? 'strict' : 'base'
2257
- var finder = Han.find( context )
2420
+ revertCorrectedBiaodian: function() {
2421
+ try {
2422
+ this.biaodian.revert( 'all' )
2423
+ } catch (e) {}
2424
+ return this
2425
+ }
2426
+ })
2258
2427
 
2259
- // Elements to be filtered according to the
2260
- // HWS rendering mode
2261
- if ( strict ) {
2262
- finder.avoid( 'textarea, code, kbd, samp, pre' )
2263
- } else {
2264
- finder.avoid( 'textarea' )
2428
+ // Legacy support (deprecated):
2429
+ Han.fn.correctBasicBD = Han.fn.correctBiaodian
2430
+ Han.fn.revertBasicBD = Han.fn.revertCorrectedBiaodian
2431
+
2432
+ var hws = '<<hws>>'
2433
+
2434
+ var $hws = $.create( 'h-hws' )
2435
+ $hws.setAttribute( 'hidden', '' )
2436
+ $hws.innerHTML = ' '
2437
+
2438
+ function sharingSameParent( $a, $b ) {
2439
+ return $a && $b && $a.parentNode === $b.parentNode
2440
+ }
2441
+
2442
+ function properlyPlaceHWSBehind( $node, text ) {
2443
+ var $elmt = $node
2444
+ var text = text || ''
2445
+
2446
+ if (
2447
+ $.isElmt( $node.nextSibling ) ||
2448
+ sharingSameParent( $node, $node.nextSibling )
2449
+ ) {
2450
+ return text + hws
2451
+ } else {
2452
+ // One of the parental elements of the current text
2453
+ // node would definitely have a next sibling, since
2454
+ // it is of the first portion and not `isEnd`.
2455
+ while ( !$elmt.nextSibling ) {
2456
+ $elmt = $elmt.parentNode
2457
+ }
2458
+ if ( $node !== $elmt ) {
2459
+ $elmt.insertAdjacentHTML( 'afterEnd', '<h-hws hidden> </h-hws>' )
2265
2460
  }
2461
+ }
2462
+ return text
2463
+ }
2266
2464
 
2267
- finder
2268
- .replace( Han.TYPESET.hws[ mode ][0], '$1<hws/>$2' )
2269
- .replace( Han.TYPESET.hws[ mode ][1], '$1<hws/>$2' )
2465
+ function firstStepLabel( portion, mat ) {
2466
+ return portion.isEnd && portion.index === 0
2467
+ ? mat[1] + hws + mat[2]
2468
+ : portion.index === 0
2469
+ ? properlyPlaceHWSBehind( portion.node, portion.text )
2470
+ : portion.text
2471
+ }
2270
2472
 
2271
- // Deal with [' 字'], [" 字"] => ['字'], ["字"]
2272
- .replace( /(['"]+)<hws\/>(.+?)<hws\/>\1/ig, '$1$2$1' )
2473
+ function real$hwsElmt( portion ) {
2474
+ return portion.index === 0
2475
+ ? $.clone( $hws )
2476
+ : ''
2477
+ }
2273
2478
 
2274
- // Remove all `<hws/>` pre/post [“字”] and [‘字’]
2275
- // See: https://github.com/ethantw/Han/issues/59
2276
- .replace( /<hws\/>([‘“]+)/ig, '$1' )
2277
- .replace( /([’”]+)<hws\/>/ig, '$1' )
2479
+ var last$hwsIdx
2278
2480
 
2279
- // Convert text nodes `<hws/>` into real element nodes
2280
- .replace( '<hws/>', function() {
2281
- return $.clone( hws )
2282
- })
2481
+ function apostrophe( portion ) {
2482
+ var $elmt = portion.node.parentNode
2283
2483
 
2284
- // Deal with:
2285
- // `漢<u><hws/>zi</u>` => `漢<hws/><u>zi</u>`
2286
- $
2287
- .qsa( QUERY_HWS_AS_FIRST_CHILD, context )
2288
- .forEach(function( firstChild ) {
2289
- var parent = firstChild.parentNode
2290
- var target = parent.firstChild
2291
-
2292
- // Skip all `<wbr>` and comments
2293
- while ( $.isIgnorable( target )) {
2294
- target = target.nextSibling
2295
-
2296
- if ( !target ) return
2297
- }
2484
+ if ( portion.index === 0 ) {
2485
+ last$hwsIdx = portion.endIndexInNode-2
2486
+ }
2298
2487
 
2299
- // The ‘first-child’ of DOM is different from
2300
- // the ones of QSA, could be either an element
2301
- // or a text fragment, but the latter one is
2302
- // not what we want. We don't want comments,
2303
- // either.
2304
- while ( target.nodeName === 'H-HWS' ) {
2305
- $.remove( target, parent )
2488
+ if (
2489
+ $elmt.nodeName.toLowerCase() === 'h-hws' && (
2490
+ portion.index === 1 || portion.indexInMatch === last$hwsIdx
2491
+ )) {
2492
+ $elmt.classList.add( 'quote-inner' )
2493
+ }
2494
+ return portion.text
2495
+ }
2306
2496
 
2307
- target = parent.parentNode.insertBefore( $.clone( hws ), parent )
2308
- parent = parent.parentNode
2497
+ function curveQuote( portion ) {
2498
+ var $elmt = portion.node.parentNode
2309
2499
 
2310
- if ( isNodeNormalizeNormal ) {
2311
- parent.normalize()
2312
- }
2500
+ if ( $elmt.nodeName.toLowerCase() === 'h-hws' ) {
2501
+ $elmt.classList.add( 'quote-outer' )
2502
+ }
2503
+ return portion.text
2504
+ }
2313
2505
 
2314
- // This is for extreme circumstances, i.e.,
2315
- // `漢<a><b><c><h-hws/>zi</c></b></a>` =>
2316
- // `漢<h-hws/><a><b><c>zi</c></b></a>`
2317
- if ( target !== parent.firstChild ) {
2318
- break
2319
- }
2320
- }
2321
- })
2506
+ $.extend( Han, {
2507
+ renderHWS: function( context, strict ) {
2508
+ // Elements to be filtered according to the
2509
+ // HWS rendering mode.
2510
+ var AVOID = strict
2511
+ ? 'textarea, code, kbd, samp, pre'
2512
+ : 'textarea'
2322
2513
 
2323
- // Normalise nodes we messed up with
2324
- if ( isNodeNormalizeNormal ) {
2325
- context.normalize()
2326
- }
2514
+ var mode = strict ? 'strict' : 'base'
2515
+ var context = context || document
2516
+ var finder = Han.find( context )
2517
+
2518
+ finder
2519
+ .avoid( AVOID )
2520
+
2521
+ // Basic situations:
2522
+ // - 字a => 字<hws/>a
2523
+ // - A字 => A<hws/>字
2524
+ .replace( Han.TYPESET.hws[ mode ][0], firstStepLabel )
2525
+ .replace( Han.TYPESET.hws[ mode ][1], firstStepLabel )
2526
+
2527
+ // Convert text nodes `<hws/>` into real element nodes:
2528
+ .replace( new RegExp( '(' + hws + ')+', 'g' ), real$hwsElmt )
2529
+
2530
+ // Deal with:
2531
+ // - '<hws/>字<hws/>' => '字'
2532
+ // - "<hws/>字<hws/>" => "字"
2533
+ .replace( /([\'"])\s(.+?)\s\1/g, apostrophe )
2534
+
2535
+ // Deal with:
2536
+ // - <hws/>“字”<hws/>
2537
+ // - <hws/>‘字’<hws/>
2538
+ .replace( /\s[‘“]/g, curveQuote )
2539
+ .replace( /[’”]\s/g, curveQuote )
2540
+ .normalize()
2327
2541
 
2328
2542
  // Return the finder instance for future usage
2329
2543
  return finder
@@ -2331,23 +2545,27 @@ $.extend( Han, {
2331
2545
  })
2332
2546
 
2333
2547
  $.extend( Han.fn, {
2334
- HWS: null,
2335
-
2336
2548
  renderHWS: function( strict ) {
2337
2549
  Han.renderHWS( this.context, strict )
2338
-
2339
- this.HWS = $.tag( 'h-hws', this.context )
2340
2550
  return this
2341
2551
  },
2342
2552
 
2343
2553
  revertHWS: function() {
2344
- this.HWS.forEach(function( hws ) {
2554
+ $.tag( 'h-hws', this.context )
2555
+ .forEach(function( hws ) {
2345
2556
  $.remove( hws )
2346
2557
  })
2558
+ this.HWS = []
2347
2559
  return this
2348
2560
  }
2349
2561
  })
2350
2562
 
2563
+ var HANGABLE_CLASS = 'bd-hangable'
2564
+ var HANGABLE_AVOID = 'h-char.bd-hangable'
2565
+ var HANGABLE_CS_HTML = '<h-cs hidden class="jinze-outer hangable-outer"> </h-cs>'
2566
+
2567
+ var matches = Han.find.matches
2568
+
2351
2569
  function detectSpaceFont() {
2352
2570
  var div = $.create( 'div' )
2353
2571
  var ret
@@ -2355,144 +2573,253 @@ function detectSpaceFont() {
2355
2573
  div.innerHTML = '<span>a b</span><span style="font-family: \'Han Space\'">a b</span>'
2356
2574
  body.appendChild( div )
2357
2575
  ret = div.firstChild.offsetWidth !== div.lastChild.offsetWidth
2358
- $.remove( div, body )
2576
+ $.remove( div )
2359
2577
  return ret
2360
2578
  }
2361
2579
 
2580
+ function insertHangableCS( $jinze ) {
2581
+ var $cs = $jinze.nextSibling
2582
+
2583
+ if ( $cs && matches( $cs, 'h-cs.jinze-outer' )) {
2584
+ $cs.classList.add( 'hangable-outer' )
2585
+ } else {
2586
+ $jinze.insertAdjacentHTML(
2587
+ 'afterend',
2588
+ HANGABLE_CS_HTML
2589
+ )
2590
+ }
2591
+ }
2592
+
2593
+ Han.support['han-space'] = detectSpaceFont()
2594
+
2362
2595
  $.extend( Han, {
2363
2596
  detectSpaceFont: detectSpaceFont,
2364
- isSpaceFontLoaded: detectSpaceFont()
2365
- })
2597
+ isSpaceFontLoaded: detectSpaceFont(),
2366
2598
 
2367
- Han.support['han-space'] = detectSpaceFont()
2599
+ renderHanging: function( context ) {
2600
+ var context = context || document
2601
+ var finder = Han.find( context )
2368
2602
 
2369
- Han.renderHanging = function( context ) {
2370
- var context = context || document
2371
- var finder = Han.find( context )
2603
+ finder
2604
+ .avoid( 'textarea, code, kbd, samp, pre' )
2605
+ .avoid( HANGABLE_AVOID )
2606
+ .replace(
2607
+ TYPESET.jinze.hanging,
2608
+ function( portion ) {
2609
+ if ( /^[\x20\t\r\n\f]+$/.test( portion.text )) {
2610
+ return ''
2611
+ }
2372
2612
 
2373
- finder
2374
- .avoid( 'textarea, code, kbd, samp, pre, h-hangable' )
2375
- .replace(
2376
- TYPESET.jinze.hanging,
2377
- function( portion, match ) {
2378
- var elem = $.create( 'h-hangable' )
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>'
2380
- return portion.index === 0 ? elem : ''
2381
- }
2382
- )
2613
+ var $elmt = portion.node.parentNode
2614
+ var $jinze, $new, $bd, biaodian
2383
2615
 
2384
- return finder
2385
- }
2616
+ if ( $jinze = $.parent( $elmt, 'h-jinze' )) {
2617
+ insertHangableCS( $jinze )
2618
+ }
2386
2619
 
2387
- $.extend( Han.fn, {
2388
- hanging: null,
2620
+ biaodian = portion.text.trim()
2389
2621
 
2390
- renderHanging: function() {
2391
- var condClazz = this.condition.classList
2392
- var isSpaceFontLoaded = detectSpaceFont()
2622
+ $new = Han.createBDChar( biaodian )
2623
+ $new.innerHTML = '<h-inner>' + biaodian + '</h-inner>'
2624
+ $new.classList.add( HANGABLE_CLASS )
2625
+
2626
+ $bd = $.parent( $elmt, 'h-char.biaodian' )
2627
+
2628
+ return !$bd
2629
+ ? $new
2630
+ : (function() {
2631
+ $bd.classList.add( HANGABLE_CLASS )
2632
+
2633
+ return matches( $elmt, 'h-inner, h-inner *' )
2634
+ ? biaodian
2635
+ : $new.firstChild
2636
+ })()
2637
+ }
2638
+ )
2639
+ return finder
2640
+ }
2641
+ })
2393
2642
 
2394
- if ( isSpaceFontLoaded && condClazz.contains( 'no-han-space' )) {
2395
- condClazz.remove( 'no-han-space' )
2396
- condClazz.add( 'han-space' )
2643
+ $.extend( Han.fn, {
2644
+ renderHanging: function() {
2645
+ var classList = this.condition.classList
2646
+ Han.isSpaceFontLoaded = detectSpaceFont()
2647
+
2648
+ if (
2649
+ Han.isSpaceFontLoaded &&
2650
+ classList.contains( 'no-han-space' )
2651
+ ) {
2652
+ classList.remove( 'no-han-space' )
2653
+ classList.add( 'han-space' )
2397
2654
  }
2398
2655
 
2399
- this.hanging = Han.renderHanging( this.context )
2656
+ Han.renderHanging( this.context )
2400
2657
  return this
2401
2658
  },
2402
2659
 
2403
2660
  revertHanging: function() {
2404
- try {
2405
- this.hanging.revert( 'all' )
2406
- } catch ( e ) {}
2661
+ $.qsa(
2662
+ 'h-char.bd-hangable, h-cs.hangable-outer',
2663
+ this.context
2664
+ ).forEach(function( $elmt ) {
2665
+ var classList = $elmt.classList
2666
+ classList.remove( 'bd-hangable' )
2667
+ classList.remove( 'hangable-outer' )
2668
+ })
2407
2669
  return this
2408
2670
  }
2409
2671
  })
2410
2672
 
2411
- Han.renderJiya = function( context ) {
2412
- var context = context || document
2413
- var finder = Han.find( context )
2673
+ var JIYA_CLASS = 'bd-jiya'
2674
+ var JIYA_AVOID = 'h-char.bd-jiya'
2675
+ var CONSECUTIVE_CLASS = 'bd-consecutive'
2676
+ var JIYA_CS_HTML = '<h-cs hidden class="jinze-outer jiya-outer"> </h-cs>'
2414
2677
 
2415
- finder
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()
2678
+ var matches = Han.find.matches
2429
2679
 
2430
- finder
2431
- .avoid( 'textarea, code, kbd, samp, pre, h-char' )
2432
- .groupify({ biaodian: true })
2433
- .charify({ biaodian: true })
2434
-
2435
- // The reason we're doing this instead of using pseudo elements in CSS
2436
- // is because WebKit has problem rendering pseudo elements containing only
2437
- // space.
2438
- $.qsa( 'h-char.biaodian.bd-open, h-char.biaodian.bd-end', context )
2439
- .forEach(function( elem ) {
2440
- if ( Han.find.matches( elem, 'h-cs *' )) return
2441
- var html = '<h-inner>' + elem.innerHTML + '</h-inner>'
2442
- var hcs = '<h-cs hidden> </h-cs>'
2443
- var isOpen = elem.classList.contains( 'bd-open' )
2444
- elem.innerHTML = isOpen ? hcs + html : html + hcs
2445
- })
2680
+ function trimBDClass( clazz ) {
2681
+ return clazz.replace(
2682
+ /(biaodian|cjk|bd-jiya|bd-consecutive|bd-hangable)/gi, ''
2683
+ ).trim()
2684
+ }
2446
2685
 
2447
- return finder
2686
+ function charifyBiaodian( portion ) {
2687
+ var biaodian = portion.text
2688
+ var $elmt = portion.node.parentNode
2689
+ var $bd = $.parent( $elmt, 'h-char.biaodian' )
2690
+ var $new = Han.createBDChar( biaodian )
2691
+ var $jinze
2692
+
2693
+ $new.innerHTML = '<h-inner>' + biaodian + '</h-inner>'
2694
+ $new.classList.add( JIYA_CLASS )
2695
+
2696
+ if ( $jinze = $.parent( $elmt, 'h-jinze' )) {
2697
+ insertJiyaCS( $jinze )
2698
+ }
2699
+
2700
+ return !$bd
2701
+ ? $new
2702
+ : (function() {
2703
+ $bd.classList.add( JIYA_CLASS )
2704
+
2705
+ return matches( $elmt, 'h-inner, h-inner *' )
2706
+ ? biaodian
2707
+ : $new.firstChild
2708
+ })()
2448
2709
  }
2449
2710
 
2450
- $.extend( Han.fn, {
2451
- jiya: null,
2711
+ var prevBDType, $$prevCS
2452
2712
 
2453
- renderJiya: function() {
2454
- this.jiya = Han.renderJiya( this.context )
2455
- return this
2456
- },
2713
+ function locateConsecutiveBD( portion ) {
2714
+ var prev = prevBDType
2715
+ var $elmt = portion.node.parentNode
2716
+ var $bd = $.parent( $elmt, 'h-char.biaodian' )
2717
+ var $jinze = $.parent( $bd, 'h-jinze' )
2718
+ var classList
2457
2719
 
2458
- revertJiya: function() {
2459
- try {
2460
- this.jiya.revert( 'all' )
2461
- } catch ( e ) {}
2462
- return this
2720
+ classList = $bd.classList
2721
+
2722
+ if ( prev ) {
2723
+ $bd.setAttribute( 'prev', prev )
2724
+ }
2725
+
2726
+ if ( $$prevCS && classList.contains( 'bd-open' )) {
2727
+ $$prevCS.pop().setAttribute( 'next', 'bd-open' )
2463
2728
  }
2464
- })
2465
2729
 
2466
- var mdot
2730
+ $$prevCS = undefined
2467
2731
 
2468
- mdot = $.create( 'h-char', 'biaodian cjk bd-middle' )
2469
- mdot.setAttribute( 'unicode', 'b7' )
2732
+ if ( portion.isEnd ) {
2733
+ prevBDType = undefined
2734
+ classList.add( CONSECUTIVE_CLASS, 'end-portion' )
2735
+ } else {
2736
+ prevBDType = trimBDClass($bd.getAttribute( 'class' ))
2737
+ classList.add( CONSECUTIVE_CLASS )
2738
+ }
2470
2739
 
2471
- Han.correctBasicBD = function( context, all ) {
2472
- if ( Han.support.unicoderange && !all ) return
2740
+ if ( $jinze ) {
2741
+ $$prevCS = locateCS( $jinze, {
2742
+ prev: prev,
2743
+ 'class': trimBDClass($bd.getAttribute( 'class' ))
2744
+ })
2745
+ }
2746
+ return portion.text
2747
+ }
2473
2748
 
2474
- var context = context || document
2475
- var finder
2749
+ function insertJiyaCS( $jinze ) {
2750
+ if (
2751
+ matches( $jinze, '.tou, .touwei' ) &&
2752
+ !matches( $jinze.previousSibling, 'h-cs.jiya-outer' )
2753
+ ) {
2754
+ $jinze.insertAdjacentHTML( 'beforebegin', JIYA_CS_HTML )
2755
+ }
2756
+ if (
2757
+ matches( $jinze, '.wei, .touwei' ) &&
2758
+ !matches( $jinze.nextSibling, 'h-cs.jiya-outer' )
2759
+ ) {
2760
+ $jinze.insertAdjacentHTML( 'afterend', JIYA_CS_HTML )
2761
+ }
2762
+ }
2763
+
2764
+ function locateCS( $jinze, attr ) {
2765
+ var $prev, $next
2766
+
2767
+ if (matches( $jinze, '.tou, .touwei' )) {
2768
+ $prev = $jinze.previousSibling
2769
+
2770
+ if (matches( $prev, 'h-cs' )) {
2771
+ $prev.className = 'jinze-outer jiya-outer'
2772
+ $prev.setAttribute( 'prev', attr.prev )
2773
+ }
2774
+ }
2775
+ if (matches( $jinze, '.wei, .touwei' )) {
2776
+ $next = $jinze.nextSibling
2777
+
2778
+ if (matches( $next, 'h-cs' )) {
2779
+ $next.className = 'jinze-outer jiya-outer ' + attr[ 'class' ]
2780
+ $next.removeAttribute( 'prev' )
2781
+ }
2782
+ }
2783
+ return [ $prev, $next ]
2784
+ }
2476
2785
 
2477
- finder = Han.find( context )
2786
+ Han.renderJiya = function( context ) {
2787
+ var context = context || document
2788
+ var finder = Han.find( context )
2478
2789
 
2479
2790
  finder
2480
- .wrap( /\u00B7/g, $.clone( mdot ))
2481
- .charify({ biaodian: true })
2791
+ .avoid( 'textarea, code, kbd, samp, pre, h-cs' )
2792
+
2793
+ .avoid( JIYA_AVOID )
2794
+ .charify({
2795
+ avoid: false,
2796
+ biaodian: charifyBiaodian
2797
+ })
2798
+ // End avoiding `JIYA_AVOID`:
2799
+ .endAvoid()
2800
+
2801
+ .avoid( 'textarea, code, kbd, samp, pre, h-cs' )
2802
+ .replace( TYPESET.group.biaodian[0], locateConsecutiveBD )
2803
+ .replace( TYPESET.group.biaodian[1], locateConsecutiveBD )
2804
+
2805
+ return finder
2482
2806
  }
2483
2807
 
2484
2808
  $.extend( Han.fn, {
2485
- basicBD: null,
2486
-
2487
- correctBasicBD: function( all ) {
2488
- this.basicBD = Han.correctBasicBD( this.context, all )
2809
+ renderJiya: function() {
2810
+ Han.renderJiya( this.context )
2489
2811
  return this
2490
2812
  },
2491
2813
 
2492
- revertBasicBD: function() {
2493
- try {
2494
- this.basicBD.revert( 'all' )
2495
- } catch (e) {}
2814
+ revertJiya: function() {
2815
+ $.qsa(
2816
+ 'h-char.bd-jiya, h-cs.jiya-outer',
2817
+ this.context
2818
+ ).forEach(function( $elmt ) {
2819
+ var classList = $elmt.classList
2820
+ classList.remove( 'bd-jiya' )
2821
+ classList.remove( 'jiya-outer' )
2822
+ })
2496
2823
  return this
2497
2824
  }
2498
2825
  })