Skip to content

Instantly share code, notes, and snippets.

@zakki
Created October 28, 2015 08:59
Show Gist options
  • Save zakki/aabc29b2883810aa67ae to your computer and use it in GitHub Desktop.
Save zakki/aabc29b2883810aa67ae to your computer and use it in GitHub Desktop.
jetzt + TinySegmenter
/*! jetzt 2015-10-28
* https://ds300.github.io/jetzt/
* Copyright (c) 2015 David Sheldrick and contributors; Licensed Apache 2.0 */
// TinySegmenter 0.1 -- Super compact Japanese tokenizer in Javascript
// (c) 2008 Taku Kudo <taku@chasen.org>
// TinySegmenter is freely distributable under the terms of a new BSD licence.
// For details, see http://chasen.org/~taku/software/TinySegmenter/LICENCE.txt
function TinySegmenter() {
var patterns = {
"[一二三四五六七八九十百千万億兆]":"M",
"[一-龠々〆ヵヶ]":"H",
"[ぁ-ん]":"I",
"[ァ-ヴーア-ン゙ー]":"K",
"[a-zA-Za-zA-Z]":"A",
"[0-90-9]":"N"
}
this.chartype_ = [];
for (var i in patterns) {
var regexp = new RegExp;
regexp.compile(i)
this.chartype_.push([regexp, patterns[i]]);
}
this.BIAS__ = -332
this.BC1__ = {"HH":6,"II":2461,"KH":406,"OH":-1378};
this.BC2__ = {"AA":-3267,"AI":2744,"AN":-878,"HH":-4070,"HM":-1711,"HN":4012,"HO":3761,"IA":1327,"IH":-1184,"II":-1332,"IK":1721,"IO":5492,"KI":3831,"KK":-8741,"MH":-3132,"MK":3334,"OO":-2920};
this.BC3__ = {"HH":996,"HI":626,"HK":-721,"HN":-1307,"HO":-836,"IH":-301,"KK":2762,"MK":1079,"MM":4034,"OA":-1652,"OH":266};
this.BP1__ = {"BB":295,"OB":304,"OO":-125,"UB":352};
this.BP2__ = {"BO":60,"OO":-1762};
this.BQ1__ = {"BHH":1150,"BHM":1521,"BII":-1158,"BIM":886,"BMH":1208,"BNH":449,"BOH":-91,"BOO":-2597,"OHI":451,"OIH":-296,"OKA":1851,"OKH":-1020,"OKK":904,"OOO":2965};
this.BQ2__ = {"BHH":118,"BHI":-1159,"BHM":466,"BIH":-919,"BKK":-1720,"BKO":864,"OHH":-1139,"OHM":-181,"OIH":153,"UHI":-1146};
this.BQ3__ = {"BHH":-792,"BHI":2664,"BII":-299,"BKI":419,"BMH":937,"BMM":8335,"BNN":998,"BOH":775,"OHH":2174,"OHM":439,"OII":280,"OKH":1798,"OKI":-793,"OKO":-2242,"OMH":-2402,"OOO":11699};
this.BQ4__ = {"BHH":-3895,"BIH":3761,"BII":-4654,"BIK":1348,"BKK":-1806,"BMI":-3385,"BOO":-12396,"OAH":926,"OHH":266,"OHK":-2036,"ONN":-973};
this.BW1__ = {",と":660,",同":727,"B1あ":1404,"B1同":542,"、と":660,"、同":727,"」と":1682,"あっ":1505,"いう":1743,"いっ":-2055,"いる":672,"うし":-4817,"うん":665,"から":3472,"がら":600,"こう":-790,"こと":2083,"こん":-1262,"さら":-4143,"さん":4573,"した":2641,"して":1104,"すで":-3399,"そこ":1977,"それ":-871,"たち":1122,"ため":601,"った":3463,"つい":-802,"てい":805,"てき":1249,"でき":1127,"です":3445,"では":844,"とい":-4915,"とみ":1922,"どこ":3887,"ない":5713,"なっ":3015,"など":7379,"なん":-1113,"にし":2468,"には":1498,"にも":1671,"に対":-912,"の一":-501,"の中":741,"ませ":2448,"まで":1711,"まま":2600,"まる":-2155,"やむ":-1947,"よっ":-2565,"れた":2369,"れで":-913,"をし":1860,"を見":731,"亡く":-1886,"京都":2558,"取り":-2784,"大き":-2604,"大阪":1497,"平方":-2314,"引き":-1336,"日本":-195,"本当":-2423,"毎日":-2113,"目指":-724,"B1あ":1404,"B1同":542,"」と":1682};
this.BW2__ = {"..":-11822,"11":-669,"――":-5730,"−−":-13175,"いう":-1609,"うか":2490,"かし":-1350,"かも":-602,"から":-7194,"かれ":4612,"がい":853,"がら":-3198,"きた":1941,"くな":-1597,"こと":-8392,"この":-4193,"させ":4533,"され":13168,"さん":-3977,"しい":-1819,"しか":-545,"した":5078,"して":972,"しな":939,"その":-3744,"たい":-1253,"たた":-662,"ただ":-3857,"たち":-786,"たと":1224,"たは":-939,"った":4589,"って":1647,"っと":-2094,"てい":6144,"てき":3640,"てく":2551,"ては":-3110,"ても":-3065,"でい":2666,"でき":-1528,"でし":-3828,"です":-4761,"でも":-4203,"とい":1890,"とこ":-1746,"とと":-2279,"との":720,"とみ":5168,"とも":-3941,"ない":-2488,"なが":-1313,"など":-6509,"なの":2614,"なん":3099,"にお":-1615,"にし":2748,"にな":2454,"によ":-7236,"に対":-14943,"に従":-4688,"に関":-11388,"のか":2093,"ので":-7059,"のに":-6041,"のの":-6125,"はい":1073,"はが":-1033,"はず":-2532,"ばれ":1813,"まし":-1316,"まで":-6621,"まれ":5409,"めて":-3153,"もい":2230,"もの":-10713,"らか":-944,"らし":-1611,"らに":-1897,"りし":651,"りま":1620,"れた":4270,"れて":849,"れば":4114,"ろう":6067,"われ":7901,"を通":-11877,"んだ":728,"んな":-4115,"一人":602,"一方":-1375,"一日":970,"一部":-1051,"上が":-4479,"会社":-1116,"出て":2163,"分の":-7758,"同党":970,"同日":-913,"大阪":-2471,"委員":-1250,"少な":-1050,"年度":-8669,"年間":-1626,"府県":-2363,"手権":-1982,"新聞":-4066,"日新":-722,"日本":-7068,"日米":3372,"曜日":-601,"朝鮮":-2355,"本人":-2697,"東京":-1543,"然と":-1384,"社会":-1276,"立て":-990,"第に":-1612,"米国":-4268,"11":-669};
this.BW3__ = {"あた":-2194,"あり":719,"ある":3846,"い.":-1185,"い。":-1185,"いい":5308,"いえ":2079,"いく":3029,"いた":2056,"いっ":1883,"いる":5600,"いわ":1527,"うち":1117,"うと":4798,"えと":1454,"か.":2857,"か。":2857,"かけ":-743,"かっ":-4098,"かに":-669,"から":6520,"かり":-2670,"が,":1816,"が、":1816,"がき":-4855,"がけ":-1127,"がっ":-913,"がら":-4977,"がり":-2064,"きた":1645,"けど":1374,"こと":7397,"この":1542,"ころ":-2757,"さい":-714,"さを":976,"し,":1557,"し、":1557,"しい":-3714,"した":3562,"して":1449,"しな":2608,"しま":1200,"す.":-1310,"す。":-1310,"する":6521,"ず,":3426,"ず、":3426,"ずに":841,"そう":428,"た.":8875,"た。":8875,"たい":-594,"たの":812,"たり":-1183,"たる":-853,"だ.":4098,"だ。":4098,"だっ":1004,"った":-4748,"って":300,"てい":6240,"てお":855,"ても":302,"です":1437,"でに":-1482,"では":2295,"とう":-1387,"とし":2266,"との":541,"とも":-3543,"どう":4664,"ない":1796,"なく":-903,"など":2135,"に,":-1021,"に、":-1021,"にし":1771,"にな":1906,"には":2644,"の,":-724,"の、":-724,"の子":-1000,"は,":1337,"は、":1337,"べき":2181,"まし":1113,"ます":6943,"まっ":-1549,"まで":6154,"まれ":-793,"らし":1479,"られ":6820,"るる":3818,"れ,":854,"れ、":854,"れた":1850,"れて":1375,"れば":-3246,"れる":1091,"われ":-605,"んだ":606,"んで":798,"カ月":990,"会議":860,"入り":1232,"大会":2217,"始め":1681,"市":965,"新聞":-5055,"日,":974,"日、":974,"社会":2024,"カ月":990};
this.TC1__ = {"AAA":1093,"HHH":1029,"HHM":580,"HII":998,"HOH":-390,"HOM":-331,"IHI":1169,"IOH":-142,"IOI":-1015,"IOM":467,"MMH":187,"OOI":-1832};
this.TC2__ = {"HHO":2088,"HII":-1023,"HMM":-1154,"IHI":-1965,"KKH":703,"OII":-2649};
this.TC3__ = {"AAA":-294,"HHH":346,"HHI":-341,"HII":-1088,"HIK":731,"HOH":-1486,"IHH":128,"IHI":-3041,"IHO":-1935,"IIH":-825,"IIM":-1035,"IOI":-542,"KHH":-1216,"KKA":491,"KKH":-1217,"KOK":-1009,"MHH":-2694,"MHM":-457,"MHO":123,"MMH":-471,"NNH":-1689,"NNO":662,"OHO":-3393};
this.TC4__ = {"HHH":-203,"HHI":1344,"HHK":365,"HHM":-122,"HHN":182,"HHO":669,"HIH":804,"HII":679,"HOH":446,"IHH":695,"IHO":-2324,"IIH":321,"III":1497,"IIO":656,"IOO":54,"KAK":4845,"KKA":3386,"KKK":3065,"MHH":-405,"MHI":201,"MMH":-241,"MMM":661,"MOM":841};
this.TQ1__ = {"BHHH":-227,"BHHI":316,"BHIH":-132,"BIHH":60,"BIII":1595,"BNHH":-744,"BOHH":225,"BOOO":-908,"OAKK":482,"OHHH":281,"OHIH":249,"OIHI":200,"OIIH":-68};
this.TQ2__ = {"BIHH":-1401,"BIII":-1033,"BKAK":-543,"BOOO":-5591};
this.TQ3__ = {"BHHH":478,"BHHM":-1073,"BHIH":222,"BHII":-504,"BIIH":-116,"BIII":-105,"BMHI":-863,"BMHM":-464,"BOMH":620,"OHHH":346,"OHHI":1729,"OHII":997,"OHMH":481,"OIHH":623,"OIIH":1344,"OKAK":2792,"OKHH":587,"OKKA":679,"OOHH":110,"OOII":-685};
this.TQ4__ = {"BHHH":-721,"BHHM":-3604,"BHII":-966,"BIIH":-607,"BIII":-2181,"OAAA":-2763,"OAKK":180,"OHHH":-294,"OHHI":2446,"OHHO":480,"OHIH":-1573,"OIHH":1935,"OIHI":-493,"OIIH":626,"OIII":-4007,"OKAK":-8156};
this.TW1__ = {"につい":-4681,"東京都":2026};
this.TW2__ = {"ある程":-2049,"いった":-1256,"ころが":-2434,"しょう":3873,"その後":-4430,"だって":-1049,"ていた":1833,"として":-4657,"ともに":-4517,"もので":1882,"一気に":-792,"初めて":-1512,"同時に":-8097,"大きな":-1255,"対して":-2721,"社会党":-3216};
this.TW3__ = {"いただ":-1734,"してい":1314,"として":-4314,"につい":-5483,"にとっ":-5989,"に当た":-6247,"ので,":-727,"ので、":-727,"のもの":-600,"れから":-3752,"十二月":-2287};
this.TW4__ = {"いう.":8576,"いう。":8576,"からな":-2348,"してい":2958,"たが,":1516,"たが、":1516,"ている":1538,"という":1349,"ました":5543,"ません":1097,"ようと":-4258,"よると":5865};
this.UC1__ = {"A":484,"K":93,"M":645,"O":-505};
this.UC2__ = {"A":819,"H":1059,"I":409,"M":3987,"N":5775,"O":646};
this.UC3__ = {"A":-1370,"I":2311};
this.UC4__ = {"A":-2643,"H":1809,"I":-1032,"K":-3450,"M":3565,"N":3876,"O":6646};
this.UC5__ = {"H":313,"I":-1238,"K":-799,"M":539,"O":-831};
this.UC6__ = {"H":-506,"I":-253,"K":87,"M":247,"O":-387};
this.UP1__ = {"O":-214};
this.UP2__ = {"B":69,"O":935};
this.UP3__ = {"B":189};
this.UQ1__ = {"BH":21,"BI":-12,"BK":-99,"BN":142,"BO":-56,"OH":-95,"OI":477,"OK":410,"OO":-2422};
this.UQ2__ = {"BH":216,"BI":113,"OK":1759};
this.UQ3__ = {"BA":-479,"BH":42,"BI":1913,"BK":-7198,"BM":3160,"BN":6427,"BO":14761,"OI":-827,"ON":-3212};
this.UW1__ = {",":156,"、":156,"「":-463,"あ":-941,"う":-127,"が":-553,"き":121,"こ":505,"で":-201,"と":-547,"ど":-123,"に":-789,"の":-185,"は":-847,"も":-466,"や":-470,"よ":182,"ら":-292,"り":208,"れ":169,"を":-446,"ん":-137,"・":-135,"主":-402,"京":-268,"区":-912,"午":871,"国":-460,"大":561,"委":729,"市":-411,"日":-141,"理":361,"生":-408,"県":-386,"都":-718,"「":-463,"・":-135};
this.UW2__ = {",":-829,"、":-829,"〇":892,"「":-645,"」":3145,"あ":-538,"い":505,"う":134,"お":-502,"か":1454,"が":-856,"く":-412,"こ":1141,"さ":878,"ざ":540,"し":1529,"す":-675,"せ":300,"そ":-1011,"た":188,"だ":1837,"つ":-949,"て":-291,"で":-268,"と":-981,"ど":1273,"な":1063,"に":-1764,"の":130,"は":-409,"ひ":-1273,"べ":1261,"ま":600,"も":-1263,"や":-402,"よ":1639,"り":-579,"る":-694,"れ":571,"を":-2516,"ん":2095,"ア":-587,"カ":306,"キ":568,"ッ":831,"三":-758,"不":-2150,"世":-302,"中":-968,"主":-861,"事":492,"人":-123,"会":978,"保":362,"入":548,"初":-3025,"副":-1566,"北":-3414,"区":-422,"大":-1769,"天":-865,"太":-483,"子":-1519,"学":760,"実":1023,"小":-2009,"市":-813,"年":-1060,"強":1067,"手":-1519,"揺":-1033,"政":1522,"文":-1355,"新":-1682,"日":-1815,"明":-1462,"最":-630,"朝":-1843,"本":-1650,"東":-931,"果":-665,"次":-2378,"民":-180,"気":-1740,"理":752,"発":529,"目":-1584,"相":-242,"県":-1165,"立":-763,"第":810,"米":509,"自":-1353,"行":838,"西":-744,"見":-3874,"調":1010,"議":1198,"込":3041,"開":1758,"間":-1257,"「":-645,"」":3145,"ッ":831,"ア":-587,"カ":306,"キ":568};
this.UW3__ = {",":4889,"1":-800,"−":-1723,"、":4889,"々":-2311,"〇":5827,"」":2670,"〓":-3573,"あ":-2696,"い":1006,"う":2342,"え":1983,"お":-4864,"か":-1163,"が":3271,"く":1004,"け":388,"げ":401,"こ":-3552,"ご":-3116,"さ":-1058,"し":-395,"す":584,"せ":3685,"そ":-5228,"た":842,"ち":-521,"っ":-1444,"つ":-1081,"て":6167,"で":2318,"と":1691,"ど":-899,"な":-2788,"に":2745,"の":4056,"は":4555,"ひ":-2171,"ふ":-1798,"へ":1199,"ほ":-5516,"ま":-4384,"み":-120,"め":1205,"も":2323,"や":-788,"よ":-202,"ら":727,"り":649,"る":5905,"れ":2773,"わ":-1207,"を":6620,"ん":-518,"ア":551,"グ":1319,"ス":874,"ッ":-1350,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278,"・":-3794,"一":-1619,"下":-1759,"世":-2087,"両":3815,"中":653,"主":-758,"予":-1193,"二":974,"人":2742,"今":792,"他":1889,"以":-1368,"低":811,"何":4265,"作":-361,"保":-2439,"元":4858,"党":3593,"全":1574,"公":-3030,"六":755,"共":-1880,"円":5807,"再":3095,"分":457,"初":2475,"別":1129,"前":2286,"副":4437,"力":365,"動":-949,"務":-1872,"化":1327,"北":-1038,"区":4646,"千":-2309,"午":-783,"協":-1006,"口":483,"右":1233,"各":3588,"合":-241,"同":3906,"和":-837,"員":4513,"国":642,"型":1389,"場":1219,"外":-241,"妻":2016,"学":-1356,"安":-423,"実":-1008,"家":1078,"小":-513,"少":-3102,"州":1155,"市":3197,"平":-1804,"年":2416,"広":-1030,"府":1605,"度":1452,"建":-2352,"当":-3885,"得":1905,"思":-1291,"性":1822,"戸":-488,"指":-3973,"政":-2013,"教":-1479,"数":3222,"文":-1489,"新":1764,"日":2099,"旧":5792,"昨":-661,"時":-1248,"曜":-951,"最":-937,"月":4125,"期":360,"李":3094,"村":364,"東":-805,"核":5156,"森":2438,"業":484,"氏":2613,"民":-1694,"決":-1073,"法":1868,"海":-495,"無":979,"物":461,"特":-3850,"生":-273,"用":914,"町":1215,"的":7313,"直":-1835,"省":792,"県":6293,"知":-1528,"私":4231,"税":401,"立":-960,"第":1201,"米":7767,"系":3066,"約":3663,"級":1384,"統":-4229,"総":1163,"線":1255,"者":6457,"能":725,"自":-2869,"英":785,"見":1044,"調":-562,"財":-733,"費":1777,"車":1835,"軍":1375,"込":-1504,"通":-1136,"選":-681,"郎":1026,"郡":4404,"部":1200,"金":2163,"長":421,"開":-1432,"間":1302,"関":-1282,"雨":2009,"電":-1045,"非":2066,"駅":1620,"1":-800,"」":2670,"・":-3794,"ッ":-1350,"ア":551,"グ":1319,"ス":874,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278};
this.UW4__ = {",":3930,".":3508,"―":-4841,"、":3930,"。":3508,"〇":4999,"「":1895,"」":3798,"〓":-5156,"あ":4752,"い":-3435,"う":-640,"え":-2514,"お":2405,"か":530,"が":6006,"き":-4482,"ぎ":-3821,"く":-3788,"け":-4376,"げ":-4734,"こ":2255,"ご":1979,"さ":2864,"し":-843,"じ":-2506,"す":-731,"ず":1251,"せ":181,"そ":4091,"た":5034,"だ":5408,"ち":-3654,"っ":-5882,"つ":-1659,"て":3994,"で":7410,"と":4547,"な":5433,"に":6499,"ぬ":1853,"ね":1413,"の":7396,"は":8578,"ば":1940,"ひ":4249,"び":-4134,"ふ":1345,"へ":6665,"べ":-744,"ほ":1464,"ま":1051,"み":-2082,"む":-882,"め":-5046,"も":4169,"ゃ":-2666,"や":2795,"ょ":-1544,"よ":3351,"ら":-2922,"り":-9726,"る":-14896,"れ":-2613,"ろ":-4570,"わ":-1783,"を":13150,"ん":-2352,"カ":2145,"コ":1789,"セ":1287,"ッ":-724,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637,"・":-4371,"ー":-11870,"一":-2069,"中":2210,"予":782,"事":-190,"井":-1768,"人":1036,"以":544,"会":950,"体":-1286,"作":530,"側":4292,"先":601,"党":-2006,"共":-1212,"内":584,"円":788,"初":1347,"前":1623,"副":3879,"力":-302,"動":-740,"務":-2715,"化":776,"区":4517,"協":1013,"参":1555,"合":-1834,"和":-681,"員":-910,"器":-851,"回":1500,"国":-619,"園":-1200,"地":866,"場":-1410,"塁":-2094,"士":-1413,"多":1067,"大":571,"子":-4802,"学":-1397,"定":-1057,"寺":-809,"小":1910,"屋":-1328,"山":-1500,"島":-2056,"川":-2667,"市":2771,"年":374,"庁":-4556,"後":456,"性":553,"感":916,"所":-1566,"支":856,"改":787,"政":2182,"教":704,"文":522,"方":-856,"日":1798,"時":1829,"最":845,"月":-9066,"木":-485,"来":-442,"校":-360,"業":-1043,"氏":5388,"民":-2716,"気":-910,"沢":-939,"済":-543,"物":-735,"率":672,"球":-1267,"生":-1286,"産":-1101,"田":-2900,"町":1826,"的":2586,"目":922,"省":-3485,"県":2997,"空":-867,"立":-2112,"第":788,"米":2937,"系":786,"約":2171,"経":1146,"統":-1169,"総":940,"線":-994,"署":749,"者":2145,"能":-730,"般":-852,"行":-792,"規":792,"警":-1184,"議":-244,"谷":-1000,"賞":730,"車":-1481,"軍":1158,"輪":-1433,"込":-3370,"近":929,"道":-1291,"選":2596,"郎":-4866,"都":1192,"野":-1100,"銀":-2213,"長":357,"間":-2344,"院":-2297,"際":-2604,"電":-878,"領":-1659,"題":-792,"館":-1984,"首":1749,"高":2120,"「":1895,"」":3798,"・":-4371,"ッ":-724,"ー":-11870,"カ":2145,"コ":1789,"セ":1287,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637};
this.UW5__ = {",":465,".":-299,"1":-514,"E2":-32768,"]":-2762,"、":465,"。":-299,"「":363,"あ":1655,"い":331,"う":-503,"え":1199,"お":527,"か":647,"が":-421,"き":1624,"ぎ":1971,"く":312,"げ":-983,"さ":-1537,"し":-1371,"す":-852,"だ":-1186,"ち":1093,"っ":52,"つ":921,"て":-18,"で":-850,"と":-127,"ど":1682,"な":-787,"に":-1224,"の":-635,"は":-578,"べ":1001,"み":502,"め":865,"ゃ":3350,"ょ":854,"り":-208,"る":429,"れ":504,"わ":419,"を":-1264,"ん":327,"イ":241,"ル":451,"ン":-343,"中":-871,"京":722,"会":-1153,"党":-654,"務":3519,"区":-901,"告":848,"員":2104,"大":-1296,"学":-548,"定":1785,"嵐":-1304,"市":-2991,"席":921,"年":1763,"思":872,"所":-814,"挙":1618,"新":-1682,"日":218,"月":-4353,"査":932,"格":1356,"機":-1508,"氏":-1347,"田":240,"町":-3912,"的":-3149,"相":1319,"省":-1052,"県":-4003,"研":-997,"社":-278,"空":-813,"統":1955,"者":-2233,"表":663,"語":-1073,"議":1219,"選":-1018,"郎":-368,"長":786,"間":1191,"題":2368,"館":-689,"1":-514,"E2":-32768,"「":363,"イ":241,"ル":451,"ン":-343};
this.UW6__ = {",":227,".":808,"1":-270,"E1":306,"、":227,"。":808,"あ":-307,"う":189,"か":241,"が":-73,"く":-121,"こ":-200,"じ":1782,"す":383,"た":-428,"っ":573,"て":-1014,"で":101,"と":-105,"な":-253,"に":-149,"の":-417,"は":-236,"も":-206,"り":187,"る":-135,"を":195,"ル":-673,"ン":-496,"一":-277,"中":201,"件":-800,"会":624,"前":302,"区":1792,"員":-1212,"委":798,"学":-960,"市":887,"広":-695,"後":535,"業":-697,"相":753,"社":-507,"福":974,"空":-822,"者":1811,"連":463,"郎":1082,"1":-270,"E1":306,"ル":-673,"ン":-496};
return this;
}
TinySegmenter.prototype.ctype_ = function(str) {
for (var i in this.chartype_) {
if (str.match(this.chartype_[i][0])) {
return this.chartype_[i][1];
}
}
return "O";
}
TinySegmenter.prototype.ts_ = function(v) {
if (v) { return v; }
return 0;
}
TinySegmenter.prototype.segment = function(input) {
if (input == null || input == undefined || input == "") {
return [];
}
var result = [];
var seg = ["B3","B2","B1"];
var ctype = ["O","O","O"];
var o = input.split("");
for (i = 0; i < o.length; ++i) {
seg.push(o[i]);
ctype.push(this.ctype_(o[i]))
}
seg.push("E1");
seg.push("E2");
seg.push("E3");
ctype.push("O");
ctype.push("O");
ctype.push("O");
var word = seg[3];
var p1 = "U";
var p2 = "U";
var p3 = "U";
for (var i = 4; i < seg.length - 3; ++i) {
var score = this.BIAS__;
var w1 = seg[i-3];
var w2 = seg[i-2];
var w3 = seg[i-1];
var w4 = seg[i];
var w5 = seg[i+1];
var w6 = seg[i+2];
var c1 = ctype[i-3];
var c2 = ctype[i-2];
var c3 = ctype[i-1];
var c4 = ctype[i];
var c5 = ctype[i+1];
var c6 = ctype[i+2];
score += this.ts_(this.UP1__[p1]);
score += this.ts_(this.UP2__[p2]);
score += this.ts_(this.UP3__[p3]);
score += this.ts_(this.BP1__[p1 + p2]);
score += this.ts_(this.BP2__[p2 + p3]);
score += this.ts_(this.UW1__[w1]);
score += this.ts_(this.UW2__[w2]);
score += this.ts_(this.UW3__[w3]);
score += this.ts_(this.UW4__[w4]);
score += this.ts_(this.UW5__[w5]);
score += this.ts_(this.UW6__[w6]);
score += this.ts_(this.BW1__[w2 + w3]);
score += this.ts_(this.BW2__[w3 + w4]);
score += this.ts_(this.BW3__[w4 + w5]);
score += this.ts_(this.TW1__[w1 + w2 + w3]);
score += this.ts_(this.TW2__[w2 + w3 + w4]);
score += this.ts_(this.TW3__[w3 + w4 + w5]);
score += this.ts_(this.TW4__[w4 + w5 + w6]);
score += this.ts_(this.UC1__[c1]);
score += this.ts_(this.UC2__[c2]);
score += this.ts_(this.UC3__[c3]);
score += this.ts_(this.UC4__[c4]);
score += this.ts_(this.UC5__[c5]);
score += this.ts_(this.UC6__[c6]);
score += this.ts_(this.BC1__[c2 + c3]);
score += this.ts_(this.BC2__[c3 + c4]);
score += this.ts_(this.BC3__[c4 + c5]);
score += this.ts_(this.TC1__[c1 + c2 + c3]);
score += this.ts_(this.TC2__[c2 + c3 + c4]);
score += this.ts_(this.TC3__[c3 + c4 + c5]);
score += this.ts_(this.TC4__[c4 + c5 + c6]);
// score += this.ts_(this.TC5__[c4 + c5 + c6]);
score += this.ts_(this.UQ1__[p1 + c1]);
score += this.ts_(this.UQ2__[p2 + c2]);
score += this.ts_(this.UQ3__[p3 + c3]);
score += this.ts_(this.BQ1__[p2 + c2 + c3]);
score += this.ts_(this.BQ2__[p2 + c3 + c4]);
score += this.ts_(this.BQ3__[p3 + c2 + c3]);
score += this.ts_(this.BQ4__[p3 + c3 + c4]);
score += this.ts_(this.TQ1__[p2 + c1 + c2 + c3]);
score += this.ts_(this.TQ2__[p2 + c2 + c3 + c4]);
score += this.ts_(this.TQ3__[p3 + c1 + c2 + c3]);
score += this.ts_(this.TQ4__[p3 + c2 + c3 + c4]);
var p = "O";
if (score > 0) {
result.push(word);
word = "";
p = "B";
}
p1 = p2;
p2 = p3;
p3 = p;
word += seg[i];
}
result.push(word);
return result;
}
(function (window) {
if (typeof window.jetzt !== 'undefined') {
console.warn("jetzt unable to initialize, window.jetzt already set");
return;
} else {
window.jetzt = {};
}
})(this);
(function (window) {
var jetzt = window.jetzt;
var H = {};
jetzt.helpers = H;
H.removeFromArray = function (arr, item) {
var pos = arr.indexOf(item);
if (pos > -1) arr.splice(pos, 1);
};
// TODO: Move dom specific stuff into separate helpers module for testing
// purposes
H.getScrollTop = function () {
return document.body.scrollTop || document.documentElement.scrollTop;
};
H.getScrollLeft = function () {
return document.body.scrollLeft || document.documentElement.scrollLeft;
};
// make an element of the specified tag and class
H.elem = function (tagName, className, kids) {
var result = document.createElement(tagName);
result.className = className || "";
if (kids) {
kids.forEach(function (kid) {result.appendChild(kid);})
}
return result;
};
H.div = function (className, kids) {
return H.elem('div', className, kids);
};
H.span = function (className, kids) {
return H.elem('span', className, kids);
};
function _modClass (elem, classes, cb) {
var elemClasses = [];
if (elem.className.trim().length >= 0) {
elemClasses = elem.className.split(/\s+/)
}
classes.split(/\s+/).forEach(function (klass) {
cb(elemClasses, klass);
});
elem.className = elemClasses.join(" ");
}
H.addClass = function (elem, classesToAdd) {
_modClass(elem, classesToAdd, function (acc, klass) {
H.removeFromArray(acc, klass);
acc.push(klass);
});
};
H.removeClass = function (elem, classesToRemove) {
_modClass(elem, classesToRemove, H.removeFromArray);
};
H.hasClass = function (elem, classesToFind) {
var found = true;
_modClass(elem, classesToFind, function (elemClassses, klass) {
found = found && elemClassses.indexOf(klass) > -1;
});
return found;
};
H.realTypeOf = function (thing) {
return Object.prototype.toString.call(thing).slice(8, -1);
};
// flatten possibly nested array
H.flatten = function (arr) {
var result = [];
var flat = function flat (thing) {
if (Object.prototype.toString.call(thing) === '[object Array]')
thing.forEach(flat);
else
result.push(thing);
};
flat(arr);
return result;
};
H.clamp = function (min, num, max) {
return Math.min(Math.max(num, min), max);
};
// merge objects together and so forth. don't rely on child object
// references being preserved.
H.recursiveExtend = function () {
var result = arguments[0];
for (var i=1; i<arguments.length; i++) {
var uber = arguments[i];
for (var prop in uber) {
if (uber.hasOwnProperty(prop)) {
if (result.hasOwnProperty(prop)) {
var resultVal = result[prop];
var uberVal = uber[prop];
if (H.realTypeOf(resultVal) === 'Object'
&& H.realTypeOf(uberVal) === 'Object') {
result[prop] = H.recursiveExtend({}, resultVal, uberVal);
} else {
result[prop] = uberVal;
}
} else {
result[prop] = uber[prop];
}
}
}
}
return result;
};
H.keys = function (obj) {
var result = [];
for (var prop in obj) { if (obj.hasOwnProperty(prop)) result.push(prop); }
return result;
};
H.clone = function (obj) {
return JSON.parse(JSON.stringify(obj));
}
})(this);
(function (window) {
var jetzt = window.jetzt
, H = jetzt.helpers;
// if you add any properties to themes, make sure to bump this
var CONFIG_VERSION = 0;
var DEFAULT_THEMES = [
{
"name": "Classic",
"dark": {
"backdrop_opacity": "0.86",
"colors": {
"backdrop": "#000000",
"background": "#303030",
"foreground": "#E0E0E0",
"message": "#909090",
"pivot": "#73b5ee",
"progress_bar_background": "#000000",
"progress_bar_foreground": "#3a5566",
"reticle": "#656565",
"wrap_background": "#404040",
"wrap_foreground": "#a1a1a1"
}
},
"light": {
"backdrop_opacity": "0.07",
"colors": {
"backdrop": "black",
"background": "#fbfbfb",
"foreground": "#333333",
"message": "#929292",
"pivot": "#E01000",
"progress_bar_background": "black",
"progress_bar_foreground": "#00c00a",
"reticle": "#efefef",
"wrap_background": "#f1f1f1",
"wrap_foreground": "#666"
}
}
}
// put more themes here
];
var DEFAULT_MODIFIERS = {
normal: 1,
start_clause: 1,
end_clause: 1.8,
start_sentence: 1.3,
end_sentence: 2.2,
start_paragraph: 2.0,
end_paragraph: 2.8,
short_space: 1.5,
long_space: 2.2
}
// Don't commit changes to these without prior approval please
var DEFAULT_OPTIONS = {
// if we change config structure in future versions, having this means
// we can update users' persisted configs to match.
config_version: CONFIG_VERSION,
target_wpm: 400,
scale: 1,
dark: false,
selected_theme: 0,
show_message: false,
selection_color: "#FF0000",
modifiers: DEFAULT_MODIFIERS,
font_family: "Menlo, Monaco, Consolas, monospace",
custom_themes: []
};
/*** STATE ***/
// This is where we store the options for the current instance of jetzt.
// The identity of the object never changes.
var options = H.clone(DEFAULT_OPTIONS);
// list of folks to notify of changes
var listeners = [];
function announce () {
listeners.forEach(function (cb) { cb(); });
}
// recursive lookup. Like clojure's get-in;
function lookup (map, keyPath) {
if (keyPath.length === 0) throw new Error("No keys specified.");
var key = keyPath[0];
if (keyPath.length === 1) {
if (!map.hasOwnProperty(key)) {
console.warn("config lookup: no key '"+key+"'");
}
return map[key];
} else {
var submap = map[key];
if (H.realTypeOf(submap) !== 'Object') {
console.warn("config lookup: no key '"+key+"'");
return;
} else {
return lookup(submap, keyPath.slice(1));
}
}
}
// recursive put. Like clojure's assoc-in
function put (map, keyPath, val) {
if (keyPath.length === 0) throw new Error("No keys specified.");
var key = keyPath[0];
if (keyPath.length === 1) {
map[key] = val;
} else {
var submap = map[key];
if (H.realTypeOf(submap) !== 'Object') {
submap = {};
map[key] = submap;
}
_put(submap, keyPath.slice(1), val);
}
}
/*** BACKEND ***/
// the backend is a swappable object with two methods, get and set.
// get takes a cb and should invoke the callback, supplying the persisted
// JSON if available, or some falsey value if not. Set takes some json and
// presumably puts it somewhere. Or not. whatevs.
// It is initialised with a localStorage placeholder for the bookmarklet and
// demo page.
var KEY = "jetzt_options";
var configBackend = {
get: function (cb) {
var json = localStorage.getItem(KEY);
if(json) {
cb("{}");
} else {
cb(json);
}
},
set: function (json) {
localStorage.setItem(KEY, json);
}
};
/*** (DE)SERIALISATION ***/
function persist () {
configBackend.set(JSON.stringify(options));
}
function unpersist (json) {
try {
var opts = JSON.parse(json || "{}")
, repersist = false;
if (opts.config_version != CONFIG_VERSION) {
// update custom themes
if (opts.custom_themes) {
H.keys(opts.custom_themes).forEach(function (id) {
var customTheme = opts.custom_themes[id];
opts.custom_themes[id] =
H.recursiveExtend(DEFAULT_THEMES.Classic, customTheme);
});
}
opts.config_version = CONFIG_VERSION;
repersist = true;
}
H.recursiveExtend(options, opts);
window.jazz = options;
repersist && persist();
announce();
} catch (e) {
throw new Error("corrupt config json", e);
}
}
/**
* jetzt.config
* get and set config variables.
*
* e.g.
* jetzt.config("cheese", "Edam")
*
* sets the "cheese" option to the string "Edam"
*
* jetzt.config("cheese")
*
* => "edam"
*
* It also has support for key paths
*
* jetzt.config(["cheese", "color"], "blue")
* jetzt.config(["cheese", "name"], "Stilton")
*
* jetzt.config(["cheese", "name"])
*
* => "Stilton"
*
* jetzt.config("cheese")
*
* => {color: "blue", name: "Stilton"}
*/
var config = function (keyPath, val) {
if (typeof keyPath === 'string') keyPath = [keyPath];
if (arguments.length === 1) {
return lookup(options, keyPath);
} else {
put(options, keyPath, val);
persist();
announce();
}
};
jetzt.config = config;
config.DEFAULTS = H.clone(DEFAULT_OPTIONS);
config.DEFAULT_THEMES = H.clone(DEFAULT_THEMES);
/**
* takes a callback and invokes it each time an option changes
* returns a function which, when invoked, unregisters the callback
*/
config.onChange = function (cb) {
listeners.push(cb);
return function () { H.removeFromArray(listeners, cb); };
};
/**
* Set the config 'backend' store. Should be an object with methods
* void get(cb(opts))
* void set(opts)
*/
config.setBackend = function (backend) {
configBackend = backend;
this.refresh();
announce();
};
/**
* Triggers an automatic reload of the persisted options
*/
config.refresh = function (cb) {
configBackend.get(function (json) {
unpersist(json);
cb && cb();
});
};
config.getSelectedTheme = function () {
return DEFAULT_THEMES[options.selected_theme] || DEFAULT_THEMES[0];
};
/**
* convenience function for finding the highest of two modifiers.
*/
config.maxModifier = function (a, b) {
return this(["modifiers", a]) > this(["modifiers", b]) ? a : b;
};
config.adjustWPM = function (diff) {
options.target_wpm = H.clamp(100, options.target_wpm + diff, 1500);
announce();
persist();
};
config.adjustScale = function (diff) {
this("scale", H.clamp(0, options.scale + diff, 1));
};
/**
* might be neccessary to trigger a save manually
*/
config.save = function () {
persist();
};
// load the options from the default config backend to get the ball rolling
config.refresh();
})(this);
(function (window) {
var jetzt = window.jetzt;
var H = jetzt.helpers;
var config = jetzt.config;
// splitting long words. Used by the Instructionator.
function wordShouldBeSplitUp(word) {
return word.length > 13 || word.length > 9 && word.indexOf("-") > -1;
}
function _maybeSplitLongWord (word) {
if (wordShouldBeSplitUp(word)) {
var result = [];
var dashIdx = word.indexOf("-");
if (dashIdx > 0 && dashIdx < word.length - 1) {
result.push(word.substr(0, dashIdx));
result.push(word.substr(dashIdx + 1));
return H.flatten(result.map(_maybeSplitLongWord));
} else {
var partitions = Math.ceil(word.length / 8);
var partitionLength = Math.ceil(word.length / partitions);
while (partitions--) {
result.push(word.substr(0, partitionLength));
word = word.substr(partitionLength);
}
return result;
}
} else {
return [word];
}
}
// split a long word into sensible sections
function splitLongWord (word) {
var result = _maybeSplitLongWord(word);
if (result.length > 1) {
for (var i=0; i<result.length-1; i++) {
result[i] += "-";
}
}
return result;
}
/**
* Helper class for generating jetzt instructions.
* Very subject to change.
*/
function Instructionator () {
// state
var instructions = []
, modifier = "normal"
, wraps = []
, spacerInstruction = null
, done = false;
// add a modifier to the next token
this.modNext = function (mod) {
modifier = config.maxModifier(modifier, mod);
};
// add a modifier to the previous token
this.modPrev = function (mod) {
if (instructions.length > 0) {
var current = instructions[instructions.length-1].modifier;
instructions[instructions.length-1].modifier = config.maxModifier(current, mod);
}
};
// start a wrap on the next token
this.pushWrap = function (wrap) {
wraps.push(wrap);
};
// stop the specified wrap before the next token.
// Pops off any wraps in the way
this.popWrap = function (wrap) {
var idx = wraps.lastIndexOf(wrap);
if (idx > -1)
wraps.splice(wraps.lastIndexOf(wrap), wraps.length);
};
// pop all wraps
this.clearWrap = function (wrap) {
wraps = [];
};
var _addWraps = function (instr) {
instr.leftWrap = wraps.map(function (w) { return w.left; }).join("");
instr.rightWrap = wraps.map(function (w) { return w.right; }).reverse().join("");
return instr;
}
// put a spacer before the next token
this.spacer = function () {
if (spacerInstruction) {
spacerInstruction.modifier = "long_space";
} else {
spacerInstruction = _addWraps({
token: " ",
modifier: "short_space"
});
}
};
var _emit = function (token) {
if (spacerInstruction) {
instructions.push(spacerInstruction);
}
instructions.push(_addWraps({
token: token,
modifier: modifier
}));
modifier = "normal";
spacerInstruction = null;
};
// add the token
this.token = function (tkn) {
if (wordShouldBeSplitUp(tkn)) {
splitLongWord(tkn).forEach(_emit);
} else {
_emit(tkn);
}
};
this.getInstructions = function () {
return instructions;
};
}
var wraps = {
guillemot: {left: "«", right: "»"},
double_quote: {left: "“", right: "”"},
parens: {left: "(", right: ")"},
heading1: {left: "H1", right: ""}
};
function parseDom(topnode,$instructionator) {
var inst = ($instructionator) ? $instructionator : new Instructionator();
var node=null;
for(var i=0;i<topnode.childNodes.length;i++) {
node=topnode.childNodes[i];
//TODO add modifiers, e.g. based on node.nodeName
switch(node.nodeName) {
case "H1":
//commented out until view for headings is implemented
//inst.pushWrap(wraps.heading1);
inst.modNext("start_paragraph");
parseDom(node,inst);
inst.spacer();
inst.clearWrap();
inst.modPrev("end_paragraph");
break;
case "SCRIPT":
break;
case "#text":
if(node.textContent.trim().length > 0) parseText(node.textContent.trim(),inst);
break;
case "P":
inst.clearWrap();
inst.modNext("start_paragraph");
parseDom(node, inst)
inst.modPrev("end_paragraph");
inst.clearWrap();
break;
case "#comment":
break;
default:
parseDom(node,inst);
}
}
return inst.getInstructions();
}
var segmenter = new TinySegmenter();
// convert raw text into instructions
function parseText (text,$instructionator) {
// long dashes ↓
var tokens = text.match(/["«»“”\(\)\/–—]|--+|\n+|[^\s"“«»”\(\)\/–—]+/g);
tokens = tokens.map(function(tkn) { return segmenter.segment(tkn); }).reduce(function (a, b) { return a.concat(b); });
var $ = ($instructionator) ? $instructionator : new Instructionator();
// doesn't handle nested double quotes, but that junk is *rare*;
var double_quote_state = false;
for (var i=0; i<tokens.length; i++) {
var tkn = tokens[i];
switch (tkn) {
case "“":
$.spacer();
$.pushWrap(wraps.double_quote);
$.modNext("start_clause");
break;
case "”":
$.popWrap(wraps.double_quote);
$.modPrev("end_clause");
$.spacer();
break;
case "«":
$.spacer();
$.pushWrap(wraps.guillemot);
$.modNext("start_clause");
break;
case "»":
$.popWrap(wraps.guillemot);
$.modPrev("end_clause");
$.spacer();
break;
case "\"":
if (double_quote_state) {
$.popWrap(wraps.double_quote)
$.spacer();
$.modNext("start_clause");
} else {
$.spacer();
$.pushWrap(wraps.double_quote);
$.modPrev("end_clause");
}
double_quote_state = !double_quote_state;
break;
case "(":
$.spacer();
$.pushWrap(wraps.parens);
$.modNext("start_clause");
break;
case ")":
$.popWrap(wraps.parens);
$.modPrev("end_clause");
$.spacer();
break;
default:
if (tkn.match(/^(\/|--+|—|–)$/)) {
$.modNext("start_clause");
$.token(tkn);
$.modNext("start_clause");
} else if (tkn.match(/[.?!…]+$/)) {
$.modNext("end_sentence");
$.token(tkn);
$.modNext("start_sentence");
} else if (tkn.match(/[,;:]$/)) {
$.modNext("end_clause");
$.token(tkn);
$.modNext("start_clause");
} else if (tkn.match(/\n+/)) {
if (tkn.length > 1
// hack for linefeed-based word wrapping. Ugly. So ugly.
|| (i > 0 && tokens[i - 1].match(/[.?!…'"”]+$/))) {
$.clearWrap();
$.modPrev("end_paragraph");
$.spacer();
$.modNext("start_paragraph");
double_quote_state = false;
}
} else if (tkn.match(/^".+$/)) {
double_quote_state = true;
$.modNext("start_clause");
$.token(tkn.substr(1));
} else {
$.token(tkn);
}
}
}
return $.getInstructions();
}
function parseStuff (parser, content) {
var instr = new Instructionator();
if (H.realTypeOf(content) === "Array") {
content.forEach(function (item) {
instr.modNext("start_paragraph");
parser(item, instr);
instr.clearWrap();
instr.modPrev("end_paragraph");
});
} else {
parser(content, instr);
}
return instr.getInstructions();
}
jetzt.parse = {
/**
* Read the given string, or array of strings.
*/
string: function (str) {
return parseStuff(parseText, str);
},
/**
* Read the given DOM element, or array of DOM elements.
*/
dom: function (dom) {
return parseStuff(parseDom, dom);
}
};
})(this);
(function (window) {
var jetzt = window.jetzt
, config = jetzt.config;
function calculateDelay(instr) {
var interval = 60 * 1000 / config("target_wpm");
if (instr.modifier !== "normal") {
return interval * config(["modifiers", instr.modifier]);
} else {
var len = instr.token.length;
var mul = 1;
switch (len) {
case 6:
case 7:
mul = 1.2;
break;
case 8:
case 9:
mul = 1.4;
break;
case 10:
case 11:
mul = 1.8;
break;
case 12:
case 13:
mul = 2;
}
return interval * mul;
}
return interval;
}
var startModifiers = {
"start_sentence": true,
"start_paragraph": true
};
/**
* Executor takes some instructions and a reader and updates the reader
* based on the start/stop/naviation methods.
*/
function Executor (instructions) {
var reader = jetzt.view.reader;
/*** STATE ***/
var running = false // whether or not the reader is running
, index = 0 // the index of the current instruction
, runLoop; // the run loop timeout
function updateReader (instr) {
if (typeof instr === "undefined") {
if (index < instructions.length) {
instr = instructions[index];
} else {
instr = instructions[instructions.length - 1];
}
}
reader.setWord(instr.token);
reader.setWrap(instr.leftWrap, instr.rightWrap);
reader.setProgress(100 * (index / instructions.length));
if (index === 1) {
startedReading();
} else if (index === instructions.length) {
finishedReading();
} else if (index % 5 === 0) {
calculateRemaining();
}
}
/**
* Calculate and display the time remaining
*/
function calculateRemaining () {
var words = instructions.length;
var timestamp = Math.round(new Date().getTime() / 1000);
var elapsed = timestamp - reader.started;
var remaining = (elapsed * (words - index)) / index;
reader.setMessage(Math.round(remaining) + "s left");
}
function startedReading () {
var timestamp = Math.round(new Date().getTime() / 1000);
reader.started = timestamp;
}
function finishedReading () {
var words = instructions.length;
var timestamp = Math.round(new Date().getTime() / 1000);
var elapsed = timestamp - reader.started;
reader.setMessage(words + " words in " + elapsed + "s");
}
function handleInstruction (instr) {
updateReader(instr);
defer(calculateDelay(instr));
}
function defer (time) {
runLoop = setTimeout(function (){
if (running && index < instructions.length) {
handleInstruction(instructions[index++]);
} else {
running = false;
}
}, time);
}
/**
* Start executing instructions
*/
this.start = function () {
if (index === instructions.length) {
index = 0;
}
running = true;
defer(0);
};
/**
* Stop executing instructions
*/
this.stop = function () {
clearTimeout(runLoop);
running = false;
};
/**
* start and stop the reader
*/
this.toggleRunning = function (run) {
if (run === running) return;
running ? this.stop() : this.start();
};
/**
* Navigate to the start of the sentence, or the start of the previous
* sentence, if less than 5 words into current sentence.
*/
this.prevSentence = function () {
index = Math.max(0, index - 5);
while (index > 0 && !startModifiers[instructions[index].modifier]) {
index--;
}
if (!running) updateReader();
};
/**
* Navigate to the start of the next sentence.
*/
this.nextSentence = function () {
index = Math.min(index+1, instructions.length - 1);
while (index < instructions.length - 1
&& !startModifiers[instructions[index].modifier]) {
index++;
}
if (!running) updateReader();
};
/**
* Navigate to the start of the paragraph, or the start of the previous
* paragraph, if less than 5 words into current paragraph
*/
this.prevParagraph = function () {
index = Math.max(0, index - 5);
while (index > 0 && instructions[index].modifier != "start_paragraph") {
index--;
}
if (!running) updateReader();
};
/**
* Navigate to the start of the next paragraph.
*/
this.nextParagraph = function () {
index = Math.min(index+1, instructions.length - 1);
while (index < instructions.length - 1
&& instructions[index].modifier != "start_paragraph") {
index++;
}
if (!running) updateReader();
};
}
/**
* jetzt.exec
* creates an instruction execution interface for a given set of
* instructions
*/
jetzt.exec = function (instructions) {
return new Executor(instructions);
};
})(this);
(function (window) {
var jetzt = window.jetzt
, config = jetzt.config
, H = jetzt.helpers
, div = H.div
, span = H.span
, view = {};
jetzt.view = view;
// calculate the focal character index
function calculatePivot (word) {
var l = word.length;
if (l < 2) {
return 0;
} else if (l < 6) {
return 1;
} else if (l < 10) {
return 2;
} else if (l < 14) {
return 3;
} else {
return 4;
}
}
function Reader () {
// elements
var backdrop = div("sr-backdrop")
, wpm = div("sr-wpm")
, leftWrap = div("sr-wrap sr-left")
, rightWrap = div("sr-wrap sr-right")
, leftWord = span()
, rightWord = span()
, pivotChar = span("sr-pivot")
, word = div("sr-word", [leftWord, pivotChar, rightWord])
, progressBar = div("sr-progress")
, message = div("sr-message")
, reticle = div("sr-reticle")
, wordBox = div("sr-word-box", [
reticle, progressBar, message, word, wpm
])
, box = div("sr-reader", [
leftWrap,
wordBox,
rightWrap
])
, wrapper = div("sr-reader-wrapper", [box])
, unlisten;
box.onkeyup = box.onkeypress = function (ev) {
if(!ev.ctrlKey && !ev.metaKey) {
ev.stopImmediatePropagation();
return false;
}
};
var grabFocus = function () {
box.tabIndex = 0; // make sure this element can have focus
box.focus();
};
this.onBackdropClick = function (cb) {
backdrop.onclick = cb;
};
this.onKeyDown = function (cb) {
box.onkeydown = cb;
};
this.dark = false;
this.applyConfig = function () {
// initialise custom size/wpm
this.dark = config("dark");
this.applyTheme(config.getSelectedTheme());
this.setScale(config("scale"));
this.setWPM(config("target_wpm"));
this.setFont(config("font_family"));
if (config("show_message")) {
this.showMessage();
} else {
this.hideMessage();
}
};
this.appendTo = function (elem) {
// fade in backdrop
elem.appendChild(backdrop);
// pull down box;
elem.appendChild(wrapper);
wrapper.offsetWidth;
H.addClass(wrapper, "in");
};
this.watchConfig = function () {
var that = this;
unlisten = config.onChange(function () { that.applyConfig(); });
};
this.unwatchConfig = function () {
unlisten && unlisten();
};
this.show = function (cb) {
this.appendTo(document.body);
// apply and listen to config;
this.applyConfig();
this.watchConfig();
// need to stop the input focus from scrolling the page up.
var scrollTop = H.getScrollTop();
grabFocus();
document.body.scrollTop = scrollTop;
document.documentElement.scrollTop = scrollTop;
box.onblur = grabFocus;
typeof cb === 'function' && window.setTimeout(cb, 340);
};
this.hide = function (cb) {
unlisten();
box.onblur = null;
box.blur();
backdrop.style.opacity = 0;
H.removeClass(wrapper, "in");
window.setTimeout(function () {
backdrop.remove();
wrapper.remove();
typeof cb === 'function' && cb();
}, 340);
};
this.setScale = function (scale) {
wrapper.style.webkitTransform = "translate(-50%, -50%) scale("+scale+")";
wrapper.style.mozTransform = "translate(-50%, -50%) scale("+scale+")";
wrapper.style.transform = "translate(-50%, -50%) scale("+scale+")";
};
this.setWPM = function (target_wpm) {
wpm.innerHTML = target_wpm + "";
};
this.setFont = function (font) {
// thanks for pointing that out
leftWord.style.fontFamily = font;
pivotChar.style.fontFamily = font;
rightWord.style.fontFamily = font;
leftWrap.style.fontFamily = font;
rightWrap.style.fontFamily = font;
wpm.style.fontFamily = font;
message.style.fontFamily = font;
};
this.applyTheme = function (theme) {
var style;
if (this.dark) {
style = theme.dark;
} else {
style = theme.light;
}
var c = style.colors;
backdrop.offsetWidth;
backdrop.style.opacity = style.backdrop_opacity;
backdrop.style.backgroundColor = c.backdrop;
wordBox.style.backgroundColor = c.background;
leftWord.style.color = c.foreground;
rightWord.style.color = c.foreground;
leftWrap.style.backgroundColor = c.wrap_background;
rightWrap.style.backgroundColor = c.wrap_background;
leftWrap.style.color = c.wrap_foreground;
rightWrap.style.color = c.wrap_foreground;
reticle.style.borderColor = c.reticle;
pivotChar.style.color = c.pivot;
progressBar.style.borderColor = c.progress_bar_foreground;
progressBar.style.backgroundColor = c.progress_bar_background;
message.style.color = c.message;
wpm.style.color = c.message;
};
this.setProgress = function (percent) {
progressBar.style.borderLeftWidth = Math.ceil(percent * 4) + "px";
};
this.setMessage = function (msg) {
message.innerHTML = msg;
};
this.showMessage = function () {
message.style.display = "block";
};
this.hideMessage = function () {
message.style.display = "none";
};
this.started = false;
this.setWord = function (token) {
var pivot = calculatePivot(token.replace(/[?.,!:;*-]+$/, ""));
leftWord.innerHTML = token.substr(0, pivot);
pivotChar.innerHTML = token.substr(pivot, 1);
rightWord.innerHTML = token.substr(pivot + 1)
word.offsetWidth;
var pivotCenter = reticle.offsetLeft + (reticle.offsetWidth / 2);
word.style.left = (pivotCenter - pivotChar.offsetLeft - (pivotChar.offsetWidth / 2)) + "px";
};
this.setWrap = function (left, right) {
leftWrap.innerHTML = left;
rightWrap.innerHTML = right;
var lw = leftWrap.offsetWidth;
var rw = rightWrap.offsetWidth;
wrapper.style.paddingLeft = "50px";
wrapper.style.paddingRight = "50px";
if (lw > rw) {
wrapper.style.paddingRight = 50 + (lw - rw) + "px";
} else if (rw > lw) {
wrapper.style.paddingLeft = 50 + (rw - lw) + "px";
}
};
this.clear = function () {
this.setWrap("", "");
this.setWord(" ");
};
}
view.Reader = Reader;
// we only need one instance of Reader now.
var readerSingleton;
view.__defineGetter__("reader", function () {
if (!readerSingleton) readerSingleton = new Reader();
return readerSingleton;
});
var overlaidElems = [];
/**
* Makes an overlay for the given element.
* Returns false if the overlay is off the bottom of the screen,
* otherwise returns true;
*/
view.addOverlay = function (elem) {
var rect = elem.getBoundingClientRect();
var overlay = H.div("sr-overlay");
overlay.style.top = (H.getScrollTop() + rect.top) + "px";
overlay.style.left = (H.getScrollLeft() + rect.left) + "px";
overlay.style.width = rect.width + "px";
overlay.style.height = rect.height + "px";
overlay.style.backgroundColor = config("selection_color");
document.body.appendChild(overlay);
elem.___jetztOverlay = overlay;
overlaidElems.push(elem);
return rect.top < window.innerHeight;
};
view.removeOverlay = function (elem) {
if (elem.___jetztOverlay) {
elem.___jetztOverlay.remove();
delete elem.___jetztOverlay;
H.removeFromArray(overlaidElems, elem);
}
};
view.removeAllOverlays = function () {
for (var i = overlaidElems.length; i--;) {
var elem = overlaidElems[i];
elem.___jetztOverlay.remove();
delete elem.___jetztOverlay;
}
overlaidElems = [];
};
var highlight;
view.highlightRange = function (range) {
// todo
};
})(this);
(function (window) {
var jetzt = window.jetzt
, H = jetzt.helpers;
function on (event, cb) {
window.addEventListener(event, cb);
}
function off (event, cb) {
window.removeEventListener(event, cb);
}
/**
* Begin interactive dom node selection.
*/
function selectMode () {
var selection = [];
var previousElement = null;
var showSelection = function () {
overlays = [];
for (var i=0, len=selection.length; i < len; i++) {
if (!jetzt.view.addOverlay(selection[i])) {
break;
}
}
};
var setSelection = function (sel) {
jetzt.view.removeAllOverlays();
selection = sel;
showSelection();
};
var validParents = {
"DIV": true,
"ARTICLE": true,
"BLOCKQUOTE": true,
"MAIN": true,
"SECTION": true,
"UL": true,
"OL": true,
"DL": true
};
var validChildren = {
"P": true,
"H1": true,
"H2": true,
"H3": true,
"H4": true,
"H5": true,
"H6": true,
"SPAN": true,
"DL": true,
"OL": true,
"UL": true,
"BLOCKQUOTE": true,
"SECTION": true
};
var selectSiblings = function (el) {
var firstChild = el;
var parent = el.parentNode;
while (parent && !validParents[parent.tagName]) {
firstChild = parent;
parent = firstChild.parentNode;
}
if (parent) {
var kids = parent.childNodes
, len = kids.length
, result = []
, i = 0;
while (kids[i] !== firstChild) i++;
for (; i < len; i++) {
var kid = kids[i];
if (validChildren[kid.tagName]) {
result.push(kid);
}
}
return result;
} else {
return [el];
}
};
var stop = function () {
jetzt.view.removeAllOverlays();
off("mouseover", mouseoverHandler);
off("mousemove", moveHandler);
off("keydown", keydownHandler);
off("keyup", keyupHandler);
off("click", clickHandler);
previousElement && H.removeClass(previousElement, "sr-pointer");
};
var mouseoverHandler = function (ev) {
previousElement && H.removeClass(previousElement, "sr-pointer");
H.addClass(ev.target, "sr-pointer");
previousElement = ev.target;
if (ev.altKey) {
setSelection([ev.target]);
} else {
setSelection(selectSiblings(ev.target));
}
};
var clickHandler = function (ev) {
stop();
jetzt.init(jetzt.parse.dom(selection));
};
var moveHandler = function (ev) {
mouseoverHandler(ev);
off("mousemove", moveHandler);
};
var keydownHandler = function (ev) {
if (ev.keyCode === 27) {
stop();
} else if (ev.altKey && selection.length > 1) {
setSelection([selection[0]]);
}
};
var keyupHandler = function (ev) {
if (!ev.altKey && selection.length === 1) {
setSelection(selectSiblings(selection[0]));
}
};
on("mouseover", mouseoverHandler);
on("click", clickHandler);
on("mousemove", moveHandler);
on("keydown", keydownHandler);
on("keyup", keyupHandler);
}
jetzt.select = function (contextData) {
var text;
if (contextData === undefined) {
text = window.getSelection().toString();
} else {
text = contextData.selectionText;
}
if (text.trim().length > 0) {
jetzt.init(jetzt.parse.string(text));
window.getSelection().removeAllRanges();
} else {
selectMode();
}
};
})(this);
(function (window) {
var jetzt = window.jetzt
, H = jetzt.helpers
, config = jetzt.config
, control = {};
jetzt.control = control;
function killEvent (ev) {
ev.preventDefault();
ev.stopImmediatePropagation();
}
/**
* hooks an executor up to keyboard controls.
*/
control.keyboard = function (executor) {
jetzt.view.reader.onKeyDown(function (ev) {
if(ev.ctrlKey || ev.metaKey) {
return;
}
// handle custom keybindings eventually
switch (ev.keyCode) {
case 27: //esc
killEvent(ev);
jetzt.quit();
break;
case 38: //up
killEvent(ev);
config.adjustWPM(+10);
break;
case 40: //down
killEvent(ev);
config.adjustWPM(-10);
break;
case 37: //left
killEvent(ev);
if (ev.altKey) executor.prevParagraph();
else executor.prevSentence();
break;
case 39: //right
killEvent(ev);
if (ev.altKey) executor.nextParagraph();
else executor.nextSentence();
break;
case 32: //space
killEvent(ev);
executor.toggleRunning();
break;
case 187: // =/+ (MSIE, Safari, Chrome)
case 107: // =/+ (Firefox, numpad)
case 61: // =/+ (Firefox, Opera)
killEvent(ev);
config.adjustScale(0.1);
break;
case 109: // -/_ (numpad, Opera, Firefox)
case 189: // -/_ (MSIE, Safari, Chrome)
case 173: // -/_ (Firefox)
killEvent(ev);
config.adjustScale(-0.1);
break;
case 48: //0 key, for changing the theme
killEvent(ev);
config("dark", !config("dark"));
break;
case 191: // / and ?
killEvent(ev);
config("show_message", !config("show_message"));
break;
}
});
};
window.addEventListener("keydown", function (ev) {
if (!jetzt.isOpen() && ev.altKey && ev.keyCode === 83) {
ev.preventDefault();
jetzt.select();
}
});
})(this);
(function (window) {
var jetzt = window.jetzt
, reader = jetzt.view.reader;
/*** state ***/
var executor;
jetzt.init = function (instructions) {
if (executor) throw new Error("jetzt already initialised");
reader.clear();
executor = jetzt.exec(instructions);
jetzt.control.keyboard(executor);
reader.show();
reader.onBackdropClick(jetzt.quit);
setTimeout(function () { executor.start(); }, 500);
};
jetzt.quit = function () {
executor.stop();
reader.hide();
executor = null;
};
jetzt.isOpen = function () {
return !!executor;
};
})(this);
@zakki
Copy link
Author

zakki commented Sep 7, 2020

javascript: (function() {var addStyle = function(url) {var style = document.createElement('link');style.rel = 'stylesheet';style.type = 'text/css';style.href = url;document.head.appendChild(style);};var addScript = function(url,cb) {var script = document.createElement('script');script.src = url;if(cb) {script.onload = cb;}document.body.appendChild(script);};if (typeof window.jetzt === 'undefined') {var cb = function() {window.jetzt.select();};addStyle('https://ds300.github.io/jetzt/jetzt.css');addScript('https://gist.githubusercontent.com/zakki/aabc29b2883810aa67ae/raw/6da68459e7bcadb53916357cc5bdfa503909d8a4/jetzt-solid.js',cb);} else {window.jetzt.select();}})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment