JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/000775 000000 000000 00000000000 11710614664 020230 5ustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/.gitignore000664 000000 000000 00000000036 11710614664 022217 0ustar00rootroot000000 000000 *~ \#*\# .DS_Store /src/dist JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/.gitmodules000664 000000 000000 00000000142 11710614664 022402 0ustar00rootroot000000 000000 [submodule "src/test/tests"] path = src/test/tests url = git://github.com/lloyd/JSONSelectTests JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/.npmignore000664 000000 000000 00000000054 11710614664 022226 0ustar00rootroot000000 000000 /site *~ /src/build /src/dist /src/Makefile JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/JSONSelect.md000664 000000 000000 00000016562 11710614664 022475 0ustar00rootroot000000 000000 **WARNING**: This document is a work in progress, just like JSONSelect itself. View or contribute to the latest version [on github](http://github.com/lloyd/JSONSelect/blob/master/JSONSelect.md) # JSONSelect 1. [introduction](#introduction) 1. [levels](#levels) 1. [language overview](#overview) 1. [grouping](#grouping) 1. [selectors](#selectors) 1. [pseudo classes](#pseudo) 1. [expressions](#expressions) 1. [combinators](#combinators) 1. [grammar](#grammar) 1. [conformance tests](#tests) 1. [references](#references) ## Introduction JSONSelect defines a language very similar in syntax and structure to [CSS3 Selectors](http://www.w3.org/TR/css3-selectors/). JSONSelect expressions are patterns which can be matched against JSON documents. Potential applications of JSONSelect include: * Simplified programmatic matching of nodes within JSON documents. * Stream filtering, allowing efficient and incremental matching of documents. * As a query language for a document database. ## Levels The specification of JSONSelect is broken into three levels. Higher levels include more powerful constructs, and are likewise more complicated to implement and use. **JSONSelect Level 1** is a small subset of CSS3. Every feature is derived from a CSS construct that directly maps to JSON. A level 1 implementation is not particularly complicated while providing basic querying features. **JSONSelect Level 2** builds upon Level 1 adapting more complex CSS constructs which allow expressions to include constraints such as patterns that match against values, and those which consider a node's siblings. Level 2 is still a direct adaptation of CSS, but includes constructs whose semantic meaning is significantly changed. **JSONSelect Level 3** adds constructs which do not necessarily have a direct analog in CSS, and are added to increase the power and convenience of the selector language. These include aliases, wholly new pseudo class functions, and more blue sky dreaming. ## Language Overview
patternmeaninglevel
*Any node1
TA node of type T, where T is one string, number, object, array, boolean, or null1
T.keyA node of type T which is the child of an object and is the value its parents key property1
T."complex key"Same as previous, but with property name specified as a JSON string1
T:rootA node of type T which is the root of the JSON document1
T:nth-child(n)A node of type T which is the nth child of an array parent1
T:nth-last-child(n)A node of type T which is the nth child of an array parent counting from the end2
T:first-childA node of type T which is the first child of an array parent (equivalent to T:nth-child(1)1
T:last-childA node of type T which is the last child of an array parent (equivalent to T:nth-last-child(1)2
T:only-childA node of type T which is the only child of an array parent2
T:emptyA node of type T which is an array or object with no child2
T UA node of type U with an ancestor of type T1
T > UA node of type U with a parent of type T1
T ~ UA node of type U with a sibling of type T2
S1, S2Any node which matches either selector S1 or S21
T:has(S)A node of type T which has a child node satisfying the selector S3
T:expr(E)A node of type T with a value that satisfies the expression E3
T:val(V)A node of type T with a value that is equal to V3
T:contains(S)A node of type T with a string value contains the substring S3
## Grouping ## Selectors ## Pseudo Classes ## Expressions ## Combinators ## Grammar (Adapted from [CSS3](http://www.w3.org/TR/css3-selectors/#descendant-combinators) and [json.org](http://json.org/)) selectors_group : selector [ `,` selector ]* ; selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ; combinator : `>` | \s+ ; simple_selector_sequence /* why allow multiple HASH entities in the grammar? */ : [ type_selector | universal ] [ class | pseudo ]* | [ class | pseudo ]+ ; type_selector : `object` | `array` | `number` | `string` | `boolean` | `null` ; universal : '*' ; class : `.` name | `.` json_string ; pseudo /* Note that pseudo-elements are restricted to one per selector and */ /* occur only in the last simple_selector_sequence. */ : `:` pseudo_class_name | `:` nth_function_name `(` nth_expression `)` | `:has` `(` selectors_group `)` | `:expr` `(` expr `)` | `:contains` `(` json_string `)` | `:val` `(` val `)` ; pseudo_class_name : `root` | `first-child` | `last-child` | `only-child` nth_function_name : `nth-child` | `nth-last-child` nth_expression /* expression is and of the form "an+b" */ : TODO ; expr : expr binop expr | '(' expr ')' | val ; binop : '*' | '/' | '%' | '+' | '-' | '<=' | '>=' | '$=' | '^=' | '*=' | '>' | '<' | '=' | '!=' | '&&' | '||' ; val : json_number | json_string | 'true' | 'false' | 'null' | 'x' ; json_string : `"` json_chars* `"` ; json_chars : any-Unicode-character-except-"-or-\-or-control-character | `\"` | `\\` | `\/` | `\b` | `\f` | `\n` | `\r` | `\t` | \u four-hex-digits ; name : nmstart nmchar* ; nmstart : escape | [_a-zA-Z] | nonascii ; nmchar : [_a-zA-Z0-9-] | escape | nonascii ; escape : \\[^\r\n\f0-9a-fA-F] ; nonascii : [^\0-0177] ; ## Conformance Tests See [https://github.com/lloyd/JSONSelectTests](https://github.com/lloyd/JSONSelectTests) ## References In no particular order. * [http://json.org/](http://json.org/) * [http://www.w3.org/TR/css3-selectors/](http://www.w3.org/TR/css3-selectors/) * [http://ejohn.org/blog/selectors-that-people-actually-use/](http://ejohn.org/blog/selectors-that-people-actually-use/) * [http://shauninman.com/archive/2008/05/05/css\_qualified\_selectors]( * http://shauninman.com/archive/2008/05/05/css_qualified_selectors) * [http://snook.ca/archives/html\_and\_css/css-parent-selectors](http://snook.ca/archives/html_and_css/css-parent-selectors) * [http://remysharp.com/2010/10/11/css-parent-selector/](http://remysharp.com/2010/10/11/css-parent-selector/) * [https://github.com/jquery/sizzle/wiki/Sizzle-Home](https://github.com/jquery/sizzle/wiki/Sizzle-Home) JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/LICENSE000664 000000 000000 00000001360 11710614664 021235 0ustar00rootroot000000 000000 Copyright (c) 2011, Lloyd Hilaiel Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/README.md000664 000000 000000 00000003777 11710614664 021525 0ustar00rootroot000000 000000 JSONSelect is *EXPERIMENTAL*, *ALPHA*, etc. JSONSelect defines a selector language similar to CSS intended for JSON documents. For an introduction to the project see [jsonselect.org](http://jsonselect.org) or the [documentation](https://github.com/lloyd/JSONSelect/blob/master/JSONSelect.md). ## Project Overview JSONSelect is an attempt to create a selector language similar to CSS for JSON objects. A couple key goals of the project's include: * **intuitive** - JSONSelect is meant to *feel like* CSS, meaning a developers with an understanding of CSS can probably guess most of the syntax. * **expressive** - As JSONSelect evolves, it will include more of the most popular constructs from the CSS spec and popular implementations (like [sizzle](http://sizzlejs.com/)). A successful result will be a good balance of simplicity and power. * **language independence** - The project will avoid features which are unnecessarily tied to a particular implementation language. * **incremental adoption** - JSONSelect features are broken in to conformance levels, to make it easier to build basic support and to allow incremental stabilization of the language. * **efficient** - As many constructs of the language as possible will be able to be evaluated in a single document traversal. This allows for efficient stream filtering. JSONSelect should make common operations easy, complex operations possible, but haughtily ignore weird shit. ## What's Here This repository is the home to many things related to JSONSelect: * [Documentation](https://github.com/lloyd/JSONSelect/blob/master/JSONSelect.md) which describes the language * The [jsonselect.org](http://jsonselect.org) [site source](https://github.com/lloyd/JSONSelect/blob/master/site/) * A [reference implementation](https://github.com/lloyd/JSONSelect/blob/master/src/jsonselect.js) in JavaScript ## Related projects Conformance tests are broken out into a [separate repository](https://github.com/lloyd/JSONSelectTests) and may be used by other implementations.JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/package.json000664 000000 000000 00000000726 11710614664 022523 0ustar00rootroot000000 000000 { "author": "Lloyd Hilaiel (http://lloyd.io)", "name": "JSONSelect", "description": "CSS-like selectors for JSON", "version": "0.4.0", "homepage": "http://jsonselect.org", "repository": { "type": "git", "url": "https://github.com/lloyd/JSONSelect.git" }, "main": "src/jsonselect", "engines": { "node": ">=0.4.7" }, "dependencies": {}, "devDependencies": {}, "scripts": { "test": "node src/test/run.js" } } JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/000775 000000 000000 00000000000 11710614664 021174 5ustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/JSONSelect.md000777 000000 000000 00000000000 11710614664 026070 2../JSONSelect.mdustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/css/000775 000000 000000 00000000000 11710614664 021764 5ustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/css/droid_sans.css000664 000000 000000 00000000262 11710614664 024623 0ustar00rootroot000000 000000 @font-face { font-family: 'Droid Sans'; font-style: normal; font-weight: normal; src: local('Droid Sans'), local('DroidSans'), url('droid_sans.tt') format('truetype'); } JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/css/droid_sans.tt000664 000000 000000 00000120104 11710614664 024460 0ustar00rootroot000000 000000  FFTMQ3(GDEFD GPOSp9dGSUBlt$ OS/2ӵe`cmapۯTcvt 9~>Lfpgms#gasp glyfI; |o(head ,6hhea d$hmtxmsTLlocaۈmaxpi nameS{post;4prep! ._<O\YssiS/Z&33f @ [(1ASC@ Ds J '7+3h{fmhRh=hRhf?R%hbhh`hRhhhqhZhjhj%%?hfhfhfh%m}y9}R+H}}'h'`7PRmm3B)J?^qqHq%%+qq1Z!# R=h3hf'hhDh{hhy3dDRhfRdm{hf1=q%#?BT?,hD}9999>R@y/}}}}}h}7?^?^?^?^?^?^^qHqHqHqHqoqqqqqhfs  mRRff??NRNR  x ~1    " : D 1    " 9 D  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpkvjsgwl|cnm}bɹyqz@EYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! ,E#F` &`&#HH-,E#F#a &a&#HH-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,F#F`F# F`ab# # pE` PXaFY`h:-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -,!! d#d@b-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-,CTXH+!!!!Y-,CTXI+!!!Y-, #KSKQZX#8!!Y-,%ISX @8!Y-,F#F`#Fa#  Fab@@pE`h:-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZX TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY@cTX@C`BYYYYY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,zE#-@ `@+ F3UU0U0o0`@8F/?O_o@FQ_+s@ F+|@ F/?O@ F/@|P|t t0ttt oooouooKo nnnnKnU?gg/g?gg@fPfff?eeedd@Od Fa_+`_G_P"[[T[[I[;[ZZkZKZ;Z3UU3U?WW/WVFV FTF@mT FRP+?POP_PHHHeHVH:HGGG;G3UU3UUGU+oTS++KRKP[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYss^stu++++++++_sssssssssss+++++_sst+++_ssssssssss++++_s^stsst++++_sssstssssststt_s+st+s+_sstt_s+_sstt_ss++sts+st+ss++s+++sss+^ NuJU}qq}}winfoxG:x Zy mmm{TooFxJ&V 8rtF"Jf " ^ P  \ $ 6j|X,B2Phd h`<z&>.n&TT v!!!"l"#Z#$$F$N%%6%%&&&''B'|''(B((())))))**(*****++ +8+P+j+++,,.,F,`,-H-`-x---. ....///2//00$0:0R0j0001D1Z1r11112D222333233344P44445(5\566~667,7J7:@ H@  H ?//+3/2]10+#34>32#".Py3"./""/."&5!!5&%5""57@# / o  ?33/3/]]]9/10#!#J)s)-)r)3@X!   P P  H   ?O   /3?399//]332233]22/]33/3/]9293/92393/3/10!!#!#!5!!5!3!3!!!?RTRNA+RR%TT#@}TTHPPH{-6?@34/))/!!p/<753.'4.'>2]T2f`T !W`e/YV*1[OdCB8JX[.+F3][(B1YSFrT7 !BUnJCoS5 *)ZBSkH!7-&b$9/&qYf3 ';?]<>@3<><>(2#(AA  0?>%7!-????/]]99//881032#"#".54>3232#"#".54>32 #GPPG$JsOIpL&#IqNKqM'GPPG#JsOJpK&#IqNKqL'՞,JHlv??vllu>>uJIHlv??vllu>>uJm}!S@M'JI,IH G6AGB B6B6B;54.#"2>7%4>7.54>32>73#'#".!4$;V8/B*Vd:bTH }4P7#B`}(MoG<-2^XS[02Tm<`+" )5A'1`l|Nis="AAC%#>@F)$=,Y(6!?HU86[A$NzdV*$MWc9KwS++SwK@m]O$73#.R$JqN%GjENqJ$1}]2w^Z=@ ??]210#>54'3$KqNEjH$NqK$1|Z^w]Rw$@?2/]/^]]10% '%7++wo`f`Fof )@   ` ?32/]]22]10!5!3!!#}}{?y 8@ +  @ H_ //]]+32]]]10%#>7j'/36z|{8=}5RBy@ @//]105!RѨ5@ 4Ddt H//+]]]]1074>32#"."./""/."o&5!!5&%5""5@ ?/382/8310 #!Jb'&@o))o  #ss??/]]10#".54>3232>54.#"3qvs93o~wt:BkMMlEElMMkBݱfffe貖KJᗖJJ5@!@n~ @ ??/^]]]3/3]10!#4>7'3ǰ`+baY"y{+`#<@ #o%%"o! " s"t?2?39/]3/3]3/310)5>54.#"'>32!p^KvS,"?V5_Ef(\jvA`l;5]K}QL;Z? M54.+532>54.#"'>32.StGAʊmUW]\W)5bYQ~U,$B\8kJ\&]n}Fln8`IxX9 `t@"-.2(JlCDa?(Jf=4R9C6}6)6a? N@, Vn  w_ t??39/322/]3]]9/]33332/]210##!533!4>7#?հ]{  eHH0d8{uf"11.*N@&o,,'$$(h#Y###@ Hs't$s ?3?9//+]3/]]333]3102#".'532>54&#"'!!>!cHDŀ3c[R!!Ybc*O|V.??9Z7' i7lir~C $ %NvQ 9]q +?7@ 1n "AA;o 6u,s's??9//]2]2104>32.#"3>32#".2>54.#"q5\ƅ./+#X+ZdC* 9L_;_l;>tfdJn#####  h88Y8(888H88C&CVCCC-s;s??9/]]]]]99/]3/]]]]2/]]99102#".54>7.54>32>54./">54&5TqB(F`8:oW5Cyfnu=-Lh:1V?%Cr DhHFkH$'If?~j}#>W30U?$~,XXClWEL_vI\h86e\Kx`JIZmBWX,5Y?##A\84TH@<Tje9R@34BT6ejj)=5@9o??/n   4u*s%u??9//]3]210#".'532>7##".54>32"32>54.5\ƅ..,#X+f+ 8L`;_l;?sfeJ%.;rjrDNG(TWFoN*/K`0CkBf'>@)))) 4Ddt@  H#/?/+]]32]1074>32#".4>32#"."./""/.""./""/."o&5!!5&%5""5'5!!5'%4""4?f a@/"""" d t P D ;  /   +  @H_ /?/]]+32]3/]]]]]]]10%#>74>32#".j'/3"./""/."6z|{8=}5'5!!5'%4""4fN@0@@o0 Pp?/^]]]q33/]]29=/33/]]10%5 d!ff\@= @ {hB9/o/]]3/^]]q/]]]]]]]]3]2105!5!fdTffN@0@@o0 Pp?/^]]]q33/]]39=/33/]]10 5f dBlfX%%';>@!2(('F F=/= -7Q?3/2/^]9/]9/3/1054>7>54.#"'>324>32#".'B20D+9U8SF?Qa]h86P64B&"./""/."%9\PM*)CEO50O94"*;3`WCiZT/-C?B,&5!!5&%5""5mJWho@?X`'''FF'N1 j@j;@NN, [d@6S@EI/3?99//^]^]322/]]q9///]]10#".'##".54>3232>54.#"32>7#"$&546$3232>?.#"%9La:-I4!6GY5MwR+;ob-ZRE"+.F/V{ZO=wod+V؂fv7jeU7N2M*Je?>}qaH)2A#%B18eVezD`5D(=hNݘOoR&,fEeՅw-SsE :^x@$FFII@ H/@_ H?2?9/9+/83^]3/8+]q39=/99]]99]]3310!!#3 .'ߢg;Dj4 Z$*[pg1111$Zd0 #`y $`"`??9/^]]92]]]9/]]q210!2#!32>54&+!2>54.#ÃB'JmEEyZ4A{oTrF XwI !K|\'Wg>lR7 -OxVdm:J;Y;xh(He=8^C%}#L@@H ` p  @ H %%[f$!_ _?3?3]3/+]]9/+]10"3267#".54>32.k{C;vvYN'NUa;LWlON?'QډۖN#ln,* . &@ [gZd``??]10#!!24.+3 `_B~uɢ ^\ՊC$ B@&g  Zd _O _ _??9/^]q229/]10)!!!!! =< p@@8 H  / Zd _?o@H@H_??9/++^]q2^]3/+]]q9/10!#!!!!}+7@++ )Zg--[ f,+_$_$_??9/]29/10!#".546$32.#"32>7!7pvKV_ oXH$SX].zB7x,I>73 ii,*Qډ؜V  =@# Ze   Zd _ ?2?39/^]2]]]210!#!#3!3պfVhRd W@& + { T + ; K   Z@ H  ?2?2/^]+]22_]]]]q10)57'5!df))ff)h)H{s/@`p/Z    _/?/^]3/]]]10"&'532>533L"N-%K=&;i{ 2XD^ie1 d@- f   /Zd  H@ H ?3?399++2]]]3/8^]33/839]310!##373=yr%3#@Zd_??]]31033!Ǻ=/@69 H9Z@ H H eO  @ H&  Z d H  H ?22+2?33+322]+^]]]993+3+2]+210]]!##!3!#46767##EAJI?9XJw4=GIQ@)(Ze'   Z dH  H ?22+?3+322]]]]2]210!###33&'.531MLA9LLJ CC> }q'4@ [g)))p)/)_)[ f(#__??]]]]10#".54>3232>54.#"qQ훣LLQ4krrk22jrrl4ݩllkk뫉ۙQQۉڗQQ3F@,[(8Hg@Zd`0@` ??9/]2^]]]]10+#!232>54&+37~Ϙj~3232>54.#"q1_]+Zyg3)LLQ4krrk22jrrl4݃ⵄ&^54.+d 1Qh7Z~Q%)SW\W]>q\#EgEHd@h3B@'Y##Zg555`5?5*Z f4*'_$ ` ?3?3992]]]3]10#"&'532654.'.54>32.#"EsoA"W`f2Iz]YU)@tawJCAXzFsT[\/aj7#"xp6PC?%#ShTX_2-#+q`9SC;!$L`~^@2  O  0 Z@Wgw@  H_??2/+]3/^]]2/]]]]]q10!#!5!!q^_/@ZeoZ d_ ?2?]]]10#".5332>7BɈąDYR(LrĐRMzH6bQ l@ `p@ H/@ H H @  H ?3?33++?3/83+]3/8+]39=/33103#3>7'*.Ja[JJa*߶HH@HHH@/H%D%%$%D%T%%% p@ H,o,, ,0,,@ H H %% H% H%?33++3?333++/83^]]]3/8+]q39=///]^]q3+3+3+3+3+3+103>73#.'&'#3>7)   ~  8pi^&&Zcg1rJ3l/7437/p6\.cb[&%blo1` @  7  8p@ H   /  @(' ?2?399]]/822/83^]3/8+]q39=/3]33]3/8310!# # 3 3`ZLN[{/L7s@  @ H@@/OZwO6?3?9]/^]]]92/8]]]33/8]]]]]3+]10 3#3TBB/R 8@ g  ? O f __?9?92/2^]22/10)5!5!!TM:ۑ9&@??/]210!!#39k1!?///8338310# J3$@`p ??]2103#5!!3jϕ)%?/3/103# )f%d!NH//3/10!5!NR! @ _/]/10#.'53x#RM?+.0SXQ"QQL^^#2T@)G#U44o40H @ H H V3P*R$P??3?9/22/++^]2210!'##".546?54.#"'>32%2>=%!BN`?EtU07Q4SB@Jdfa0/=hL+ZzI a-A*'Q{TECZ70"(8)Yb&MuOc 9Q3\V?/8-HW11@ I%GT0*P  P?2?3??22+102#".'##33>"32654&^m<32.#"3267ReJLfN268<:Q66{?Ֆۉ>"  %q04@&GU22.H V1+P P?3?3??]2210%##".54>323&'.53#%2>754.#"T;M`<]n<32!32>7"!4.`nHBxecn;L3WQL'(MQW`r 9XJ҇֕NGnq ۜDqP,p@N`?OG/ OP ???^]32/^]3/]322/]9/]]]10###5754>32.#"3-U|N;c'/I((:&?KD`kT# 0SAh%^?R^@ 2SG7/`7p777/7/'HYG@M H  0@`````@'@ H'2 7.5467.54>3232654.+"32654&#"&/_],!)8]Q$A͋kj5'BW/*6@E+G12ba%O@;aH7ZA#L?)\lcdgidcjJq#mEL^5  (!/Pm=Xa4*PqG<[B* R5=Y*?Q`3Yb4 %@.sl.:! ,M`spow{tx2@GU` G TP ?2??322]10!4&#"#33>32\ipQnC ER\0Â4f`2+?*3u%@  GTS??3/22]10!#34632#"&d=-'?,-=J)<6 +:98u!.@# #G  T"S P??3/2/22]10"&'532>534632#"&B0?6#.#"Hm=-'?,-= 'A3M{W/_<6 +:98^@ D@ H/ G T @ H H ??399++?2^]3/8+]333931073 ##3V%om7i%RZ6d@ GT??]10!#3d^,e@?# G   g w  G,U... .P..GT-#P( ?22??32232^]]]]9/]]]]210!4&#"#4&#"#33>323>32diIfAciMh? BOY.x&IW`2Â/[XÂ4f`J+?*X^/D-3^0@GU` G TP  ?2??32]10!4&#"#33>32\ipQnC ER\0Â4f`J+?*3q-^0@HW!@!!!!H V PP??^]]10#".54>3232654&#"-C}ogGC|ogG'ՑLLՉӑKKӈ?^06@.HW22& G T1 P +P?2???3222]10".'##33>32"32654&;`M; :M`<^m<754.#"".54>32373#46767#5LiAAlQf]n<H;?hK)9GX^3_QJ+P=%Z?^5H@-%GW7?7_777,G V6&)P," P?2?992]2]]]310#"&'532>54.'.54>32.#"?:m`m;LTY,A[95\HHsP+7dVaH?AGfb8^FHqP*-PxQ(#");$212#4./7#".5#5?3!!-*# (04>jM,Ni?  Ne}QNabJ0@GU`G T P??3?3]210!'##".5332>53u ER\0[\/joQnC+?).bi=4e`:Jm@ H H H@ HP/O@ G  ??39]/8^]]]3/8++9=/3+3+10!33>73w  ǼJ!hl``lh!cJ/ù/@ H/ H' @ H  H  H@ HT''@ H[  H'  '-.H.@ H...1 1011@-  'fv?33]3?3]33/83^]]3/8++39=///+]+]3+3+3+3+3+3+10!.'&'##33>733>73   翃  Ĭ   h-24:>?:2j%J-ig[Wa_!k"\_XWhm/H#J @ 6 9k{W:JdtX5E   6@H@Hk{W:J  0        ; K (    ??/]]]]]]]^]]q]]]++]]]q9=///]]]]]]]]]q3]33]]]]q10]] 33 # #u3fL J"d"H@ H$$$$P$$/$O$@ "#P?2?333/83^]]]3/8++9=/331033>73#"&'532>?  ǼNAVtP4L@#0F4%9J(XXR#Va^!c'QZ1 ,@)R5J l@  H@ H ? _   HH?@ HO HO?2+?2+/]+33+]]3/+3+]310)5!5!!5 }D='@@% '#  #_)??9/]]9/]332/210.54ᒑ4>7-A(M_6}}6_M(A-wssw0=# !GnNNgVVgMNnG! #=0i{ zj-@0@p@??/^]]q103#閖3)@@% $$$# ??9/]]9/]332/2104675.54.'53"5>5wssw-A(M_6!A`>}6_M(A-;jz {iL0=# !GnN4H-VgNnG! #=0fJZ#<@ %%   @H  ? O o  /]3/+2/]]10.#"563232>7#".%7-)<;8d27C/%7/(<;8c27C !,l  !,l ^A@ H0@ H /^]//+3/2]10+3##".54>32y3#..##..#H&5!!5&%4""4%Z@%F %'@'H 0 @  s!s@ H??99//+33/^]]29/3210$#5.54>753.#"3267vnLWb45aVH.58<;Q6 KljˈK !  %D#(u@ o# H@0 H**!@ H)!u /"""""""""ts??9/]323/+3]3/++399//^]32102.#"!!!!5>=#534>jBB8K0RY@+ )DaCՉDW_2{#7@#. !p99 $@1 H8  ) 3   ? o  /]]]2/]93]23/+]2]3/]9/]210467'7>327'#"&''7.732>54.#"#b/l<7.54>32.#"#"&'532>54.'.7>54.'-:KU7dVaH8AGcf9_FHqN*)4EL;l`l;LTY+E]73^LIsP)?eH#)!AlR/&)3S@-&rT=bD%( ';9.,/ANa>4UD1&mNGoM(! '3--1>NdY%?:7 $.8"&@;9-:3 j 5@! @P 0  /]]32/^]]104632#"&%4632#"&38('::'(8w8(#:&(8s6015522560 &522dD%AUj@C"""&L444WB& /`p-G;Q-?/99//]^]/]q99//3/10"32>7#".54>32.4>32#".732>54.#"{=^@!=_C69815<#fe36id?;>4a6ahha66ahha6meꅅeeꅅe,SxKNxR+  BzgexC!ha66ahhb55bheeꅅeeDB-N@/-///O///$ `  .-'?99/]2/]]2210'#".546?54&#"'>3232>='/8#+H4c=80Z*03u<}w3D)2*":+R# 3M3flH9d$jz:9+3-,A,1Rs `@ P`   @! H    /3/39=/93333/]]+299//]310%R5uu6tt)NNNNf9@$yVK8 ?/]]]]]]]]]10#!5RBydD:N@}R  E---P;/`p&@4J&??99//]^]q3392/]q99//]^]q929++]10]32654&+###324>32#".732>54.#"H[OSYF-9C5*! _騞6ahha66ahha6meꅅeeꅅeHEJ;0K9( nW%G8`}ha66ahhb55bheeꅅee//3/10!5! {V'C@, ))0@ o #?3/^]]]q/]]104>32#".732>54.#"{2UsAAsV22VsAAsU2{4F((F55F((F4AsV22VsAArU11UrA'E44E'(G55Gf :@!  ` ?32//]]333223]10!5!3!!#5!}}}{1Jm@@ O   @ H@H ??9/+3/+]210!57>54&#"'>32!m9H(B63]-N6RUC;"A@2&^0A!?[92VU[79h0a@<2_222@ H''@ H/_&#, ?3?39/^]9/+3/+]3/9/910#"&'532654&+532654.#"'>32NQEXX(S~VF{9?5bXk`bb\T#/;a3E=DL,EiF#NjjN73#//*?MQ#yLQQ"QXSJ7@" G U `pGTP  ?3???2]21032>53#'##"&'#3djoRnC 0gHj#4e`:ST.*&(#U*6qf7@!0@P    /2?/]]9/^]10####".54>3!fxy=U_m32#"."./""/."&5!!5&%4""4#9@ @ H //9/+9/]33310#"&'532654.'73-1GP.?%Zy9":+all+1# s):?J4@!O@ H 0 ??/]]33/+]103#4>7'3&^J<<8(I`B.@ H!! ?]+10#".54>3232654&#")MmD?jN+)LmD>kN,:KVUKKUVKmSY//YSSX..XSwyywxssTs V@/    @     /3/399=//3333/]]299//3]10 '7'7tt6huu5eN\\NbeN\\Nb?&{'J0@?@@]5]]5]]]55?55,&{'5t3(@@p@]]5]]5]5?5&u'?<@'8p8P88333d3P303 33L]]]]]]]]5]]55?55DwD^';D@2(('F == F@H ''-7Q/3?2/9/+^]9/3/103267#".54>7>=#".54>32P'A20D+9U7TE@Ra]g85Q64B&#..##..#%:[QL*)CEO50O93#*:3`XDhZT/-C>C+/&5!!5&%4""4s&$CR&%+5+5s&$vR@ !&l%+5+5s&$R&%+5+55&$R@ &,%+5+5+&$j!R@ &)%+55+55&$}1@ P@ %+55]]]]]]]55V@* Z$4T  g@  __ _ O    _?3/?99//^]q2/8329///]]]]}32310)!#!!!!!!#V%˺=ul;<}&&z O*$ %+5s&(CR &´ %+5+5s&(v?R@ &J %+5+5s&(R & %+5+5+&(jR@ & %+55+55>ds&,CR & %+5+5Rs&,vxR@ &j %+5+5s&,R@  & %+5+5@w+&,j R@ & %+55+55/]@:[g! !Zd _?o@H``??9/+^]q3222/2]9/103!2#!#%4.+!!3 /_`B~uP %\^`ՊC$5&1R@  & !/ %+5+5}qs&2CTR(&.( %+5+5}qs&2vR@ 4&X(. %+5+5}qs&2R@ (&0( %+5+5}q5&2}R0&1? %+5+5}q+&2jR@ 1&(< %+55+55-{ H@HH H H HH@0H@  P   Pp ?^]q2323/]3333]10++++++++ 7   'i=Bh?fg?i>gf=g}q&1\@:)*'[ g333p3/3_3[f2)*-"_  -_ ?3?399]]]]9910#"''7&54>327.#"'32>\[^Q훽NZa[L^BP.0C0rGrl4jX/rErk2c޷lGNd*k*&N QڊTQs&8C=R& %+5+5s&8vR@ $&H %+5+5s&8yR&  %+5+5+&8j}R@ !&, %+55+557s&<v1R@ &c %+5+53<@![g Zd``    ??99//22]]10+#33232>54&+37~Ϙ~54.'.54>54.#"#4>32+?K?+'F98X=!8eUa5AHL%8Q4+H8?U5)>H>)!W~Q'#"-@($;8:#(DCF*6O?6:C,*>)0SANhU%&Lt^!&DC3&93 "%+5+5^!&Dv5@ ?&39 "%+5+5^!&D@ 3&3;3 "%+5+5^&DŽ@ ;&)32>32!32>7#"&'#".732>="!4.^7Q4SB@Jd+3gal9`1UNJ%'KOU1>"L_tJG{Z4aO=hL+ZzI n 7T3ECZ70"(8U]U]Gnq rs6U;'Q{R\V&MuOc 9QcDqP,qo^&FzB /&  %+5q!&HC(&.(%+5+5q!&HvR@ 4&v(.%+5+5q!&H@ (&0(%+5+5q&Hj@ 1&(<%+55+55g!&CU& %+5+5B!&v0@ &t %+5+5U!&@ & %+5+5%&j@  &%+55+55o-#'9t@F(H# """ W;@;;;;2H V: #!!-P07P??99//]]339^]]9///9210#".54>327.''7.'774.#"326-C}ohG?vif+xZJ(U/FAz;JCoO,"FnKMmF!!GmL=ܘOBww~A;<vQr7{ H,quAݰ8kR2.XUL}Z1&Q@ !&"0 %+5+5q-!&RC &״& %+5+5q-!&RvP@ ,&N & %+5+5q-!&R &( %+5+5q-&R(&)7 %+5+5q-&Rj)& 4 %+55+55f+`@0-"Vf(8@( H'  `?3/^]3/]q/3+3]]3/]105!4>32#".4>32#".f)*  *))*  *)#/ /#!//#/ /#!//s/$-\@;'(%H  W/@////H V.('+"P +P??99^]]9910#"''7.54>327.#"4'326/C}o}bDP?FC|o?q1DP>EK-D'rH-'ՑL5mJHՉӑKlIIцT3џc{!&XC&! %+5+5!&Xv`@ '&W! %+5+5!&X@ &# %+5+5&Xj$&/ %+55+55 !&\v@ /&g#)%+5+5? 18@/H W33' GT2,P!P?3?3??2222]10>32#".'##3%"32654&d:M`<^m<73y3l46j3yDC;;CE"a77a"LQQ""QQLm1@@-?O_0  ?O__/]^]/]]10#".54>324&#"3261#=T12R; ;R20T>#u?12?981?3Q88O33O87O45<<55<<8@#/  @H@ H /]23/++3/^]]10".#"#>3232673(OLF -0h!5J.*QLE-.i!5J#+#5>73%'.46z|{8=|5P %@_ _o?/]/3]10#>7B'/37y}z8<|5?y 5@ H _oH?/+33/]+10%#>7j'/36z|{8=}5 b@H_o_o P`p_o ?32/]33/]/3/]3]]]10'>73!'>73'.4'.46z|{8=|56z|{8=|5 b@H_oP`p_o _o ?22/33/]/]3/]3]]]10#>7!#>7B'/3H'/37y}z8<|57y}z8<|5? ~@Q 0@`pP`p_ _o@ H ?22/+33/]/]]3/]3]_]]]10%#>7!#>7j'/3H'/36z|{8=}56z|{8=}5mF@$/_o_ o  @  H/]]/+]]]]104>32#".$?V21V@%%@V12V?$Gd??dGFd??dRs<@ H?//9=/33/]]+210R5uu)NNRs?@( ??//9=/33/]3]/]]10 '7uu5eN\\Nbh@ ??/82/8310 #h՝+J J F@*  _@ H   /  ??9/^]32/+]9/33210##5!533!5467}y}  oC*c1 %*(n4j  0i N< g < . .D*   h   *  ,;   (  8 \Y \ Ts Digitized data copyright 2007, Google Corporation.Digitized data copyright 2007, Google Corporation.Droid SansDroid SansRegularRegularAscender - Droid SansAscender - Droid SansDroid SansDroid SansVersion 1.00 build 113Version 1.00 build 113DroidSansDroidSansDroid is a trademark of Google and may be registered in certain jurisdictions.Droid is a trademark of Google and may be registered in certain jurisdictions.Ascender CorporationAscender CorporationDroid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.Droid Sans is a humanist sans serif typeface designed for user interfaces and electronic communication.http://www.ascendercorp.com/http://www.ascendercorp.com/http://www.ascendercorp.com/typedesigners.htmlhttp://www.ascendercorp.com/typedesigners.htmlLicensed under the Apache License, Version 2.0Licensed under the Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0http://www.apache.org/licenses/LICENSE-2.0Droid SansDroid Sansff  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~uni00AD overscore foursuperior3O ,latnkernfPjhJ@Fp h " " ( " : ` v | " " ( ( > ` ` *j  hhhhTrrrrrJ. " " " " " " (xxxx *HH,$,)7R9R:f;)<R=)FGHJRTW)Y)Z\)\))))R))-{&*24789:<7 &*24789:<,79;<) ) )&*24@)`)))$,79:;<== = )")$9:<@)`)==) )&*24))) )&*24)) &*24789:<$,79:;<=33$&;<=q$,79:;<=7JR R")$&*2467DFGHJPQRSTUVXYZ[\]qRR $>R R")$&*24DFGHJPQRSTUVXRR>f f$&*24DFGHJPQRSTUVX]ff ) )&*24FGHRT))DR R")$&*246DFGHJPQRSTUVX[\]qRR = === f fYZ\ff) )J)) ) )))[] f fDJffR RWRRR RIRR ) )R))= =I==R' DD"&*-^24789:<=;IWYZ\% DD"&*24789:<=;IWYZ\% DD"&*-^24789:<=;WYZ\'f f DD"&*-^24789:<=;WYZ\ff) )&24))) )&24))$ $,-679:;<=@`$0=DRR R = )")$&*-02467'9):@=DFGHIJPQRSTUVXYZ[\]`=qRR  o oI[] = ="FGHIJRTW== = =I==)$,)7R9R:f;)<R=)FGHJRTW)Y)Z\))))R $')) ,, ./ 257>DFHKNN!PR"UW%Y^(.4>CIU[abe latnJSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/css/style.css000664 000000 000000 00000005763 11710614664 023651 0ustar00rootroot000000 000000 body { background-color: #191919; color: #a1a1a1; font-family: 'Droid Sans', arial, serif; font-size: 14px; } #header { position: fixed; background-color: #191919; opacity: .7; color: #ccc; width: 100%; top: 0px; left: 0px; margin: 0px; padding: 4px 10px 4px 10px; } #main { margin-top: 100px; } #header b { font-weight: bold; color: #fff; } a { color: #999; text-decoration: underline; } a:hover { color: #fff; } #header .title { font-size: 400%; } #header .subtitle { font-size: 150%; margin-left: 1em; font-style: italic; } #header .nav { float: right; margin-right: 100px; } div.content { max-width: 850px; min-width: 675px; margin: auto; } div#tryit { min-width: 825px; } #splash { max-width: 700px; } .json { background-color: #333; padding: .5em 1em .5em 1em; border: 3px solid #444; border-radius: 1em; -moz-border-radius: 1em; -webkit-border-radius: 1em; } #splash .sample { width: 250px; float: right; margin-left: 1em; margin-top: 3em; font-size: 90%; } #doc { margin-top: 140px; } #doc table { width: 100%; } #doc table th { background-color: #aaa; color: #333; padding: 10px; } #doc table tr td:first-child { font-family: "andale mono",monospace; } #doc table td { padding-top: 3px; } #splash .sample tt { font-size: 90%; padding: 0 .2em 0 .2em; } #splash .sample pre { border-top: 1px dashed #aaa; padding-top: 1em; } #splash .pitch { padding-top: 2em; font-size: 170%; } .selected { opacity: .7; padding: 8px; margin: -10px; background-color: #fff475; color: #000; border: 2px solid white; border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; } div.current input, div.selector, pre, code, tt { font-family: "andale mono",monospace; font-size: .9em; } div.current { width: 100%; padding: 1em; } div.current input { color: #fff; font-size: 110%; background-color: #333; border: 1px solid #444; padding: 8px; width: 400px; margin-left: 1em; margin-right: 1em; border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; } pre.doc { float: right; padding: 1em 3em 1em 3em; padding-right: 160px; font-size: 90%; } .selectors { margin: 1em; background-color: #444; width: 300px; padding: 8px; border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; } .selectors .selector { background-color: #333; padding: .4em; margin: 1px; cursor: pointer; } .selectors .selector.inuse { border: 1px solid #9f9; margin: -1px; margin-left: 0px; } .results.error { color: #f99; } .results { color: #9f9; font-size: 90%; width: 300px; } #cred, #code { padding-top: 2em; } p.psst { margin-top: 4em; font-size: .9em; } p.item { margin-top: 2em; margin-left: 1em; } JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/index.html000664 000000 000000 00000014275 11710614664 023202 0ustar00rootroot000000 000000 JSONSelect
Fork me on GitHub JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/js/000775 000000 000000 00000000000 11710614664 021610 5ustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/js/demo.js000664 000000 000000 00000004561 11710614664 023100 0ustar00rootroot000000 000000 window.jsel = JSONSelect; $(document).ready(function() { var theDoc = JSON.parse($("pre.doc").text()); function highlightMatches(ar) { // first calculate match offsets var wrk = []; var html = $.trim(JSON.stringify(theDoc, undefined, 4)); var ss = ""; var es = ""; for (var i = 0; i < ar.length; i++) { var found = $.trim(JSON.stringify(ar[i], undefined, 4)); // turn the string into a regex to handle indentation found = found.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&").replace(/\s+/gm, "\\s*"); var re = new RegExp(found, "m"); var m = re.exec(html); if (!m) continue; wrk.push({ off: m.index, typ: "s" }); wrk.push({ off: m[0].length+m.index, typ: "e" }); } // sort by offset wrk = wrk.sort(function(a,b) { return a.off - b.off; }); // now start injecting spans into the text var cur = 0; var cons = 0; for (var i = 0; i < wrk.length; i++) { var diff = wrk[i].off - cons; cons = wrk[i].off; var tag = (wrk[i].typ == 's' ? ss : es); cur += diff; html = html.substr(0, cur) + tag + html.substr(cur); cur += tag.length; } return html; } // when a selector is chosen, update the text box $(".selectors .selector").click(function() { $(".current input").val($(this).text()).keyup(); }); var lastSel; $(".current input").keyup(function () { try { var sel = $(".current input").val() if (lastSel === $.trim(sel)) return; lastSel = $.trim(sel); var ar = jsel.match(sel, theDoc); $(".current .results").text(ar.length + " match" + (ar.length == 1 ? "" : "es")) .removeClass("error"); $("pre.doc").html(highlightMatches(ar)); $("pre.doc .selected").hide().fadeIn(700); } catch(e) { $(".current .results").text(e.toString()).addClass("error"); $("pre.doc").text($.trim(JSON.stringify(theDoc, undefined, 4))); } $(".selectors .selector").removeClass("inuse"); $(".selectors div.selector").each(function() { if ($(this).text() === sel) $(this).addClass("inuse"); }); }); }); JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/js/json2.js000664 000000 000000 00000042745 11710614664 023215 0ustar00rootroot000000 000000 /* http://www.JSON.org/json2.js 2011-02-23 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. See http://www.JSON.org/js.html This code should be minified before deployment. See http://javascript.crockford.com/jsmin.html USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO NOT CONTROL. This file creates a global JSON object containing two methods: stringify and parse. JSON.stringify(value, replacer, space) value any JavaScript value, usually an object or array. replacer an optional parameter that determines how object values are stringified for objects. It can be a function or an array of strings. space an optional parameter that specifies the indentation of nested structures. If it is omitted, the text will be packed without extra whitespace. If it is a number, it will specify the number of spaces to indent at each level. If it is a string (such as '\t' or ' '), it contains the characters used to indent at each level. This method produces a JSON text from a JavaScript value. When an object value is found, if the object contains a toJSON method, its toJSON method will be called and the result will be stringified. A toJSON method does not serialize: it returns the value represented by the name/value pair that should be serialized, or undefined if nothing should be serialized. The toJSON method will be passed the key associated with the value, and this will be bound to the value For example, this would serialize Dates as ISO strings. Date.prototype.toJSON = function (key) { function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } return this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z'; }; You can provide an optional replacer method. It will be passed the key and value of each member, with this bound to the containing object. The value that is returned from your method will be serialized. If your method returns undefined, then the member will be excluded from the serialization. If the replacer parameter is an array of strings, then it will be used to select the members to be serialized. It filters the results such that only members with keys listed in the replacer array are stringified. Values that do not have JSON representations, such as undefined or functions, will not be serialized. Such values in objects will be dropped; in arrays they will be replaced with null. You can use a replacer function to replace those with JSON values. JSON.stringify(undefined) returns undefined. The optional space parameter produces a stringification of the value that is filled with line breaks and indentation to make it easier to read. If the space parameter is a non-empty string, then that string will be used for indentation. If the space parameter is a number, then the indentation will be that many spaces. Example: text = JSON.stringify(['e', {pluribus: 'unum'}]); // text is '["e",{"pluribus":"unum"}]' text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' text = JSON.stringify([new Date()], function (key, value) { return this[key] instanceof Date ? 'Date(' + this[key] + ')' : value; }); // text is '["Date(---current time---)"]' JSON.parse(text, reviver) This method parses a JSON text to produce an object or array. It can throw a SyntaxError exception. The optional reviver parameter is a function that can filter and transform the results. It receives each of the keys and values, and its return value is used instead of the original value. If it returns what it received, then the structure is not modified. If it returns undefined then the member is deleted. Example: // Parse the text. Values that look like ISO date strings will // be converted to Date objects. myData = JSON.parse(text, function (key, value) { var a; if (typeof value === 'string') { a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); if (a) { return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])); } } return value; }); myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { var d; if (typeof value === 'string' && value.slice(0, 5) === 'Date(' && value.slice(-1) === ')') { d = new Date(value.slice(5, -1)); if (d) { return d; } } return value; }); This is a reference implementation. You are free to copy, modify, or redistribute. */ /*jslint evil: true, strict: false, regexp: false */ /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length, parse, prototype, push, replace, slice, stringify, test, toJSON, toString, valueOf */ // Create a JSON object only if one does not already exist. We create the // methods in a closure to avoid creating global variables. var JSON; if (!JSON) { JSON = {}; } (function () { "use strict"; function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } if (typeof Date.prototype.toJSON !== 'function') { Date.prototype.toJSON = function (key) { return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z' : null; }; String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) { return this.valueOf(); }; } var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = { // table of character substitutions '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }, rep; function quote(string) { // If the string contains no control characters, no quote characters, and no // backslash characters, then we can safely slap some quotes around it. // Otherwise we must also replace the offending characters with safe escape // sequences. escapable.lastIndex = 0; return escapable.test(string) ? '"' + string.replace(escapable, function (a) { var c = meta[a]; return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; } function str(key, holder) { // Produce a string from holder[key]. var i, // The loop counter. k, // The member key. v, // The member value. length, mind = gap, partial, value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value. if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); } // If we were called with a replacer function, then call the replacer to // obtain a replacement value. if (typeof rep === 'function') { value = rep.call(holder, key, value); } // What happens next depends on the value's type. switch (typeof value) { case 'string': return quote(value); case 'number': // JSON numbers must be finite. Encode non-finite numbers as null. return isFinite(value) ? String(value) : 'null'; case 'boolean': case 'null': // If the value is a boolean or null, convert it to a string. Note: // typeof null does not produce 'null'. The case is included here in // the remote chance that this gets fixed someday. return String(value); // If the type is 'object', we might be dealing with an object or an array or // null. case 'object': // Due to a specification blunder in ECMAScript, typeof null is 'object', // so watch out for that case. if (!value) { return 'null'; } // Make an array to hold the partial results of stringifying this object value. gap += indent; partial = []; // Is the value an array? if (Object.prototype.toString.apply(value) === '[object Array]') { // The value is an array. Stringify every element. Use null as a placeholder // for non-JSON values. length = value.length; for (i = 0; i < length; i += 1) { partial[i] = str(i, value) || 'null'; } // Join all of the elements together, separated with commas, and wrap them in // brackets. v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } // If the replacer is an array, use it to select the members to be stringified. if (rep && typeof rep === 'object') { length = rep.length; for (i = 0; i < length; i += 1) { if (typeof rep[i] === 'string') { k = rep[i]; v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } else { // Otherwise, iterate through all of the keys in the object. for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } // Join all of the member texts together, separated with commas, // and wrap them in braces. v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}'; gap = mind; return v; } } // If the JSON object does not yet have a stringify method, give it one. if (typeof JSON.stringify !== 'function') { JSON.stringify = function (value, replacer, space) { // The stringify method takes a value and an optional replacer, and an optional // space parameter, and returns a JSON text. The replacer can be a function // that can replace values, or an array of strings that will select the keys. // A default replacer method can be provided. Use of the space parameter can // produce text that is more easily readable. var i; gap = ''; indent = ''; // If the space parameter is a number, make an indent string containing that // many spaces. if (typeof space === 'number') { for (i = 0; i < space; i += 1) { indent += ' '; } // If the space parameter is a string, it will be used as the indent string. } else if (typeof space === 'string') { indent = space; } // If there is a replacer, it must be a function or an array. // Otherwise, throw an error. rep = replacer; if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) { throw new Error('JSON.stringify'); } // Make a fake root object containing our value under the key of ''. // Return the result of stringifying the value. return str('', {'': value}); }; } // If the JSON object does not yet have a parse method, give it one. if (typeof JSON.parse !== 'function') { JSON.parse = function (text, reviver) { // The parse method takes a text and an optional reviver function, and returns // a JavaScript value if the text is a valid JSON text. var j; function walk(holder, key) { // The walk method is used to recursively walk the resulting structure so // that modifications can be made. var k, v, value = holder[key]; if (value && typeof value === 'object') { for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } } return reviver.call(holder, key, value); } // Parsing happens in four stages. In the first stage, we replace certain // Unicode characters with escape sequences. JavaScript handles many characters // incorrectly, either silently deleting them, or treating them as line endings. text = String(text); cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }); } // In the second stage, we run the text against regular expressions that look // for non-JSON patterns. We are especially concerned with '()' and 'new' // because they can cause invocation, and '=' because it can cause mutation. // But just to be safe, we want to reject all unexpected forms. // We split the second stage into 4 regexp operations in order to work around // crippling inefficiencies in IE's and Safari's regexp engines. First we // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we // replace all simple value tokens with ']' characters. Third, we delete all // open brackets that follow a colon or comma or that begin the text. Finally, // we look to see that the remaining characters are only whitespace or ']' or // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. if (/^[\],:{}\s]*$/ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { // In the third stage we use the eval function to compile the text into a // JavaScript structure. The '{' operator is subject to a syntactic ambiguity // in JavaScript: it can begin a block or an object literal. We wrap the text // in parens to eliminate the ambiguity. j = eval('(' + text + ')'); // In the optional fourth stage, we recursively walk the new structure, passing // each name/value pair to a reviver function for possible transformation. return typeof reviver === 'function' ? walk({'': j}, '') : j; } // If the text is not JSON parseable, then a SyntaxError is thrown. throw new SyntaxError('JSON.parse'); }; } }()); JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/js/jsonselect.js000777 000000 000000 00000000000 11710614664 030236 2../../src/jsonselect.jsustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/js/main.js000664 000000 000000 00000000044 11710614664 023070 0ustar00rootroot000000 000000 $(document).ready(function() { }); JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/js/nav.js000664 000000 000000 00000004613 11710614664 022736 0ustar00rootroot000000 000000 $(document).ready(function() { var docsLoaded = false; $(window).hashchange(function(e){ e.preventDefault(); e.stopPropagation(); if (location.hash === "#tryit") { $("#main > .content").hide(); $("#tryit input").val("").keyup(); $("#tryit").fadeIn(400, function() { $("#tryit input").val(".languagesSpoken .lang").keyup(); }); } else if (location.hash === "#cred") { $("#main > .content").hide(); $("#cred").fadeIn(400); } else if (location.hash === '#overview' || location.hash === '') { $("#main > .content").hide(); $("#splash").fadeIn(400); } else if (location.hash === '#code' || location.hash === '') { $("#main > .content").hide(); $("#code").fadeIn(400); } else if (location.hash.substr(0,5) === "#docs") { function showIt() { var where = window.location.hash.substr(6); if (!where) { $("#doc").fadeIn(400); } else { $("#doc").show(); var dst = $("a[name='" + where + "']"); if (dst.length) { $('html, body').animate({scrollTop:dst.offset().top - 100}, 500); } } } $("#main > .content").hide(); if (!docsLoaded) { $.get("JSONSelect.md").success(function(data) { var converter = new Showdown.converter(); $("#doc").html(converter.makeHtml(data)); $("#doc a").each(function() { var n = $(this).attr('href'); if (typeof n === 'string' && n.substr(0,1) === '#') { $(this).attr('href', "#docs/" + n.substr(1)); } }); docsLoaded = true; showIt(); }).error(function() { $("#doc").text("Darnit, error fetching docs...").fadeIn(400); }); } else { showIt(); } } else { } return false; }); // Trigger the event (useful on page load). if (window.location.hash === "") window.location.hash = "#overview"; $(window).hashchange(); }); JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/site/js/showdown.js000664 000000 000000 00000105040 11710614664 024016 0ustar00rootroot000000 000000 // // showdown.js -- A javascript port of Markdown. // // Copyright (c) 2007 John Fraser. // // Original Markdown Copyright (c) 2004-2005 John Gruber // // // Redistributable under a BSD-style open source license. // See license.txt for more information. // // The full source distribution is at: // // A A L // T C A // T K B // // // // // Wherever possible, Showdown is a straight, line-by-line port // of the Perl version of Markdown. // // This is not a normal parser design; it's basically just a // series of string substitutions. It's hard to read and // maintain this way, but keeping Showdown close to the original // design makes it easier to port new features. // // More importantly, Showdown behaves like markdown.pl in most // edge cases. So web applications can do client-side preview // in Javascript, and then build identical HTML on the server. // // This port needs the new RegExp functionality of ECMA 262, // 3rd Edition (i.e. Javascript 1.5). Most modern web browsers // should do fine. Even with the new regular expression features, // We do a lot of work to emulate Perl's regex functionality. // The tricky changes in this file mostly have the "attacklab:" // label. Major or self-explanatory changes don't. // // Smart diff tools like Araxis Merge will be able to match up // this file with markdown.pl in a useful way. A little tweaking // helps: in a copy of markdown.pl, replace "#" with "//" and // replace "$text" with "text". Be sure to ignore whitespace // and line endings. // // // Showdown usage: // // var text = "Markdown *rocks*."; // // var converter = new Showdown.converter(); // var html = converter.makeHtml(text); // // alert(html); // // Note: move the sample code to the bottom of this // file before uncommenting it. // // // Showdown namespace // var Showdown = {}; // // converter // // Wraps all "globals" so that the only thing // exposed is makeHtml(). // Showdown.converter = function() { // // Globals: // // Global hashes, used by various utility routines var g_urls; var g_titles; var g_html_blocks; // Used to track when we're inside an ordered or unordered list // (see _ProcessListItems() for details): var g_list_level = 0; this.makeHtml = function(text) { // // Main function. The order in which other subs are called here is // essential. Link and image substitutions need to happen before // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the // and tags get encoded. // // Clear the global hashes. If we don't clear these, you get conflicts // from other articles when generating a page which contains more than // one article (e.g. an index page that shows the N most recent // articles): g_urls = new Array(); g_titles = new Array(); g_html_blocks = new Array(); // attacklab: Replace ~ with ~T // This lets us use tilde as an escape char to avoid md5 hashes // The choice of character is arbitray; anything that isn't // magic in Markdown will work. text = text.replace(/~/g,"~T"); // attacklab: Replace $ with ~D // RegExp interprets $ as a special character // when it's in a replacement string text = text.replace(/\$/g,"~D"); // Standardize line endings text = text.replace(/\r\n/g,"\n"); // DOS to Unix text = text.replace(/\r/g,"\n"); // Mac to Unix // Make sure text begins and ends with a couple of newlines: text = "\n\n" + text + "\n\n"; // Convert all tabs to spaces. text = _Detab(text); // Strip any lines consisting only of spaces and tabs. // This makes subsequent regexen easier to write, because we can // match consecutive blank lines with /\n+/ instead of something // contorted like /[ \t]*\n+/ . text = text.replace(/^[ \t]+$/mg,""); // Turn block-level HTML blocks into hash entries text = _HashHTMLBlocks(text); // Strip link definitions, store in hashes. text = _StripLinkDefinitions(text); text = _RunBlockGamut(text); text = _UnescapeSpecialChars(text); // attacklab: Restore dollar signs text = text.replace(/~D/g,"$$"); // attacklab: Restore tildes text = text.replace(/~T/g,"~"); return text; } var _StripLinkDefinitions = function(text) { // // Strips link definitions from text, stores the URLs and titles in // hash references. // // Link defs are in the form: ^[id]: url "optional title" /* var text = text.replace(/ ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 [ \t]* \n? // maybe *one* newline [ \t]* ? // url = $2 [ \t]* \n? // maybe one newline [ \t]* (?: (\n*) // any lines skipped = $3 attacklab: lookbehind removed ["(] (.+?) // title = $4 [")] [ \t]* )? // title is optional (?:\n+|$) /gm, function(){...}); */ var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm, function (wholeMatch,m1,m2,m3,m4) { m1 = m1.toLowerCase(); g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive if (m3) { // Oops, found blank lines, so it's not a title. // Put back the parenthetical statement we stole. return m3+m4; } else if (m4) { g_titles[m1] = m4.replace(/"/g,"""); } // Completely remove the definition from the text return ""; } ); return text; } var _HashHTMLBlocks = function(text) { // attacklab: Double up blank lines to reduce lookaround text = text.replace(/\n/g,"\n\n"); // Hashify HTML blocks: // We only want to do this for block-level HTML tags, such as headers, // lists, and tables. That's because we still want to wrap

s around // "paragraphs" that are wrapped in non-block-level tags, such as anchors, // phrase emphasis, and spans. The list of tags we're looking for is // hard-coded: var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del" var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math" // First, look for nested blocks, e.g.: //

//
// tags for inner block must be indented. //
//
// // The outermost tags must start at the left margin for this to match, and // the inner nested divs must be indented. // We need to do this before the next, more liberal match, because the next // match will start at the first `
` and stop at the first `
`. // attacklab: This regex can be expensive when it fails. /* var text = text.replace(/ ( // save in $1 ^ // start of line (with /m) <($block_tags_a) // start tag = $2 \b // word break // attacklab: hack around khtml/pcre bug... [^\r]*?\n // any number of lines, minimally matching // the matching end tag [ \t]* // trailing spaces/tabs (?=\n+) // followed by a newline ) // attacklab: there are sentinel newlines at end of document /gm,function(){...}}; */ text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement); // // Now match more liberally, simply from `\n` to `\n` // /* var text = text.replace(/ ( // save in $1 ^ // start of line (with /m) <($block_tags_b) // start tag = $2 \b // word break // attacklab: hack around khtml/pcre bug... [^\r]*? // any number of lines, minimally matching .* // the matching end tag [ \t]* // trailing spaces/tabs (?=\n+) // followed by a newline ) // attacklab: there are sentinel newlines at end of document /gm,function(){...}}; */ text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement); // Special case just for
. It was easier to make a special case than // to make the other regex more complicated. /* text = text.replace(/ ( // save in $1 \n\n // Starting after a blank line [ ]{0,3} (<(hr) // start tag = $2 \b // word break ([^<>])*? // \/?>) // the matching end tag [ \t]* (?=\n{2,}) // followed by a blank line ) /g,hashElement); */ text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement); // Special case for standalone HTML comments: /* text = text.replace(/ ( // save in $1 \n\n // Starting after a blank line [ ]{0,3} // attacklab: g_tab_width - 1 [ \t]* (?=\n{2,}) // followed by a blank line ) /g,hashElement); */ text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g,hashElement); // PHP and ASP-style processor instructions ( and <%...%>) /* text = text.replace(/ (?: \n\n // Starting after a blank line ) ( // save in $1 [ ]{0,3} // attacklab: g_tab_width - 1 (?: <([?%]) // $2 [^\r]*? \2> ) [ \t]* (?=\n{2,}) // followed by a blank line ) /g,hashElement); */ text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement); // attacklab: Undo double lines (see comment at top of this function) text = text.replace(/\n\n/g,"\n"); return text; } var hashElement = function(wholeMatch,m1) { var blockText = m1; // Undo double lines blockText = blockText.replace(/\n\n/g,"\n"); blockText = blockText.replace(/^\n/,""); // strip trailing blank lines blockText = blockText.replace(/\n+$/g,""); // Replace the element text with a marker ("~KxK" where x is its key) blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n"; return blockText; }; var _RunBlockGamut = function(text) { // // These are all the transformations that form block-level // tags like paragraphs, headers, and list items. // text = _DoHeaders(text); // Do Horizontal Rules: var key = hashBlock("
"); text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key); text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key); text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key); text = _DoLists(text); text = _DoCodeBlocks(text); text = _DoBlockQuotes(text); // We already ran _HashHTMLBlocks() before, in Markdown(), but that // was to escape raw HTML in the original Markdown source. This time, // we're escaping the markup we've just created, so that we don't wrap //

tags around block-level tags. text = _HashHTMLBlocks(text); text = _FormParagraphs(text); return text; } var _RunSpanGamut = function(text) { // // These are all the transformations that occur *within* block-level // tags like paragraphs, headers, and list items. // text = _DoCodeSpans(text); text = _EscapeSpecialCharsWithinTagAttributes(text); text = _EncodeBackslashEscapes(text); // Process anchor and image tags. Images must come first, // because ![foo][f] looks like an anchor. text = _DoImages(text); text = _DoAnchors(text); // Make links out of things like `` // Must come after _DoAnchors(), because you can use < and > // delimiters in inline links like [this](). text = _DoAutoLinks(text); text = _EncodeAmpsAndAngles(text); text = _DoItalicsAndBold(text); // Do hard breaks: text = text.replace(/ +\n/g,"
\n"); return text; } var _EscapeSpecialCharsWithinTagAttributes = function(text) { // // Within tags -- meaning between < and > -- encode [\ ` * _] so they // don't conflict with their use in Markdown for code, italics and strong. // // Build a regex to find HTML tags and comments. See Friedl's // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; text = text.replace(regex, function(wholeMatch) { var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`"); tag = escapeCharacters(tag,"\\`*_"); return tag; }); return text; } var _DoAnchors = function(text) { // // Turn Markdown link shortcuts into XHTML
tags. // // // First, handle reference-style links: [link text] [id] // /* text = text.replace(/ ( // wrap whole match in $1 \[ ( (?: \[[^\]]*\] // allow brackets nested one level | [^\[] // or anything else )* ) \] [ ]? // one optional space (?:\n[ ]*)? // one optional newline followed by spaces \[ (.*?) // id = $3 \] )()()()() // pad remaining backreferences /g,_DoAnchors_callback); */ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag); // // Next, inline-style links: [link text](url "optional title") // /* text = text.replace(/ ( // wrap whole match in $1 \[ ( (?: \[[^\]]*\] // allow brackets nested one level | [^\[\]] // or anything else ) ) \] \( // literal paren [ \t]* () // no id, so leave $3 empty ? // href = $4 [ \t]* ( // $5 (['"]) // quote char = $6 (.*?) // Title = $7 \6 // matching quote [ \t]* // ignore any spaces/tabs between closing quote and ) )? // title is optional \) ) /g,writeAnchorTag); */ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag); // // Last, handle reference-style shortcuts: [link text] // These must come last in case you've also got [link test][1] // or [link test](/foo) // /* text = text.replace(/ ( // wrap whole match in $1 \[ ([^\[\]]+) // link text = $2; can't contain '[' or ']' \] )()()()()() // pad rest of backreferences /g, writeAnchorTag); */ text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); return text; } var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { if (m7 == undefined) m7 = ""; var whole_match = m1; var link_text = m2; var link_id = m3.toLowerCase(); var url = m4; var title = m7; if (url == "") { if (link_id == "") { // lower-case and turn embedded newlines into spaces link_id = link_text.toLowerCase().replace(/ ?\n/g," "); } url = "#"+link_id; if (g_urls[link_id] != undefined) { url = g_urls[link_id]; if (g_titles[link_id] != undefined) { title = g_titles[link_id]; } } else { if (whole_match.search(/\(\s*\)$/m)>-1) { // Special case for explicit empty url url = ""; } else { return whole_match; } } } url = escapeCharacters(url,"*_"); var result = ""; return result; } var _DoImages = function(text) { // // Turn Markdown image shortcuts into tags. // // // First, handle reference-style labeled images: ![alt text][id] // /* text = text.replace(/ ( // wrap whole match in $1 !\[ (.*?) // alt text = $2 \] [ ]? // one optional space (?:\n[ ]*)? // one optional newline followed by spaces \[ (.*?) // id = $3 \] )()()()() // pad rest of backreferences /g,writeImageTag); */ text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag); // // Next, handle inline images: ![alt text](url "optional title") // Don't forget: encode * and _ /* text = text.replace(/ ( // wrap whole match in $1 !\[ (.*?) // alt text = $2 \] \s? // One optional whitespace character \( // literal paren [ \t]* () // no id, so leave $3 empty ? // src url = $4 [ \t]* ( // $5 (['"]) // quote char = $6 (.*?) // title = $7 \6 // matching quote [ \t]* )? // title is optional \) ) /g,writeImageTag); */ text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag); return text; } var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { var whole_match = m1; var alt_text = m2; var link_id = m3.toLowerCase(); var url = m4; var title = m7; if (!title) title = ""; if (url == "") { if (link_id == "") { // lower-case and turn embedded newlines into spaces link_id = alt_text.toLowerCase().replace(/ ?\n/g," "); } url = "#"+link_id; if (g_urls[link_id] != undefined) { url = g_urls[link_id]; if (g_titles[link_id] != undefined) { title = g_titles[link_id]; } } else { return whole_match; } } alt_text = alt_text.replace(/"/g,"""); url = escapeCharacters(url,"*_"); var result = "\""' + _RunSpanGamut(m1) + "");}); text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, function(matchFound,m1){return hashBlock('

' + _RunSpanGamut(m1) + "

");}); // atx-style headers: // # Header 1 // ## Header 2 // ## Header 2 with closing hashes ## // ... // ###### Header 6 // /* text = text.replace(/ ^(\#{1,6}) // $1 = string of #'s [ \t]* (.+?) // $2 = Header text [ \t]* \#* // optional closing #'s (not counted) \n+ /gm, function() {...}); */ text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, function(wholeMatch,m1,m2) { var h_level = m1.length; return hashBlock("' + _RunSpanGamut(m2) + ""); }); function headerId(m) { return m.replace(/[^\w]/g, '').toLowerCase(); } return text; } // This declaration keeps Dojo compressor from outputting garbage: var _ProcessListItems; var _DoLists = function(text) { // // Form HTML ordered (numbered) and unordered (bulleted) lists. // // attacklab: add sentinel to hack around khtml/safari bug: // http://bugs.webkit.org/show_bug.cgi?id=11231 text += "~0"; // Re-usable pattern to match any entirel ul or ol list: /* var whole_list = / ( // $1 = whole list ( // $2 [ ]{0,3} // attacklab: g_tab_width - 1 ([*+-]|\d+[.]) // $3 = first list item marker [ \t]+ ) [^\r]+? ( // $4 ~0 // sentinel for workaround; should be $ | \n{2,} (?=\S) (?! // Negative lookahead for another list item marker [ \t]* (?:[*+-]|\d+[.])[ \t]+ ) ) )/g */ var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; if (g_list_level) { text = text.replace(whole_list,function(wholeMatch,m1,m2) { var list = m1; var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol"; // Turn double returns into triple returns, so that we can make a // paragraph for the last item in a list, if necessary: list = list.replace(/\n{2,}/g,"\n\n\n");; var result = _ProcessListItems(list); // Trim any trailing whitespace, to put the closing `` // up on the preceding line, to get it past the current stupid // HTML block parser. This is a hack to work around the terrible // hack that is the HTML block parser. result = result.replace(/\s+$/,""); result = "<"+list_type+">" + result + "\n"; return result; }); } else { whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) { var runup = m1; var list = m2; var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol"; // Turn double returns into triple returns, so that we can make a // paragraph for the last item in a list, if necessary: var list = list.replace(/\n{2,}/g,"\n\n\n");; var result = _ProcessListItems(list); result = runup + "<"+list_type+">\n" + result + "\n"; return result; }); } // attacklab: strip sentinel text = text.replace(/~0/,""); return text; } _ProcessListItems = function(list_str) { // // Process the contents of a single ordered or unordered list, splitting it // into individual list items. // // The $g_list_level global keeps track of when we're inside a list. // Each time we enter a list, we increment it; when we leave a list, // we decrement. If it's zero, we're not in a list anymore. // // We do this because when we're not inside a list, we want to treat // something like this: // // I recommend upgrading to version // 8. Oops, now this line is treated // as a sub-list. // // As a single paragraph, despite the fact that the second line starts // with a digit-period-space sequence. // // Whereas when we're inside a list (or sub-list), that line will be // treated as the start of a sub-list. What a kludge, huh? This is // an aspect of Markdown's syntax that's hard to parse perfectly // without resorting to mind-reading. Perhaps the solution is to // change the syntax rules such that sub-lists must start with a // starting cardinal number; e.g. "1." or "a.". g_list_level++; // trim trailing blank lines: list_str = list_str.replace(/\n{2,}$/,"\n"); // attacklab: add sentinel to emulate \z list_str += "~0"; /* list_str = list_str.replace(/ (\n)? // leading line = $1 (^[ \t]*) // leading whitespace = $2 ([*+-]|\d+[.]) [ \t]+ // list marker = $3 ([^\r]+? // list item text = $4 (\n{1,2})) (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+)) /gm, function(){...}); */ list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, function(wholeMatch,m1,m2,m3,m4){ var item = m4; var leading_line = m1; var leading_space = m2; if (leading_line || (item.search(/\n{2,}/)>-1)) { item = _RunBlockGamut(_Outdent(item)); } else { // Recursion for sub-lists: item = _DoLists(_Outdent(item)); item = item.replace(/\n$/,""); // chomp(item) item = _RunSpanGamut(item); } return "
  • " + item + "
  • \n"; } ); // attacklab: strip sentinel list_str = list_str.replace(/~0/g,""); g_list_level--; return list_str; } var _DoCodeBlocks = function(text) { // // Process Markdown `
    ` blocks.
    //  
    
    	/*
    		text = text.replace(text,
    			/(?:\n\n|^)
    			(								// $1 = the code block -- one or more lines, starting with a space/tab
    				(?:
    					(?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
    					.*\n+
    				)+
    			)
    			(\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
    		/g,function(){...});
    	*/
    
    	// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
    	text += "~0";
    	
    	text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
    		function(wholeMatch,m1,m2) {
    			var codeblock = m1;
    			var nextChar = m2;
    		
    			codeblock = _EncodeCode( _Outdent(codeblock));
    			codeblock = _Detab(codeblock);
    			codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
    			codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
    
    			codeblock = "
    " + codeblock + "\n
    "; return hashBlock(codeblock) + nextChar; } ); // attacklab: strip sentinel text = text.replace(/~0/,""); return text; } var hashBlock = function(text) { text = text.replace(/(^\n+|\n+$)/g,""); return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n"; } var _DoCodeSpans = function(text) { // // * Backtick quotes are used for spans. // // * You can use multiple backticks as the delimiters if you want to // include literal backticks in the code span. So, this input: // // Just type ``foo `bar` baz`` at the prompt. // // Will translate to: // //

    Just type foo `bar` baz at the prompt.

    // // There's no arbitrary limit to the number of backticks you // can use as delimters. If you need three consecutive backticks // in your code, use four for delimiters, etc. // // * You can use spaces to get literal backticks at the edges: // // ... type `` `bar` `` ... // // Turns to: // // ... type `bar` ... // /* text = text.replace(/ (^|[^\\]) // Character before opening ` can't be a backslash (`+) // $2 = Opening run of ` ( // $3 = The code block [^\r]*? [^`] // attacklab: work around lack of lookbehind ) \2 // Matching closer (?!`) /gm, function(){...}); */ text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, function(wholeMatch,m1,m2,m3,m4) { var c = m3; c = c.replace(/^([ \t]*)/g,""); // leading whitespace c = c.replace(/[ \t]*$/g,""); // trailing whitespace c = _EncodeCode(c); return m1+""+c+""; }); return text; } var _EncodeCode = function(text) { // // Encode/escape certain characters inside Markdown code runs. // The point is that in code, these characters are literals, // and lose their special Markdown meanings. // // Encode all ampersands; HTML entities are not // entities within a Markdown code span. text = text.replace(/&/g,"&"); // Do the angle bracket song and dance: text = text.replace(//g,">"); // Now, escape characters that are magic in Markdown: text = escapeCharacters(text,"\*_{}[]\\",false); // jj the line above breaks this: //--- //* Item // 1. Subitem // special char: * //--- return text; } var _DoItalicsAndBold = function(text) { // must go first: text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, "$2"); text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, "$2"); return text; } var _DoBlockQuotes = function(text) { /* text = text.replace(/ ( // Wrap whole match in $1 ( ^[ \t]*>[ \t]? // '>' at the start of a line .+\n // rest of the first line (.+\n)* // subsequent consecutive lines \n* // blanks )+ ) /gm, function(){...}); */ text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, function(wholeMatch,m1) { var bq = m1; // attacklab: hack around Konqueror 3.5.4 bug: // "----------bug".replace(/^-/g,"") == "bug" bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting // attacklab: clean up hack bq = bq.replace(/~0/g,""); bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines bq = _RunBlockGamut(bq); // recurse bq = bq.replace(/(^|\n)/g,"$1 "); // These leading spaces screw with
     content, so we need to fix that:
    			bq = bq.replace(
    					/(\s*
    [^\r]+?<\/pre>)/gm,
    				function(wholeMatch,m1) {
    					var pre = m1;
    					// attacklab: hack around Konqueror 3.5.4 bug:
    					pre = pre.replace(/^  /mg,"~0");
    					pre = pre.replace(/~0/g,"");
    					return pre;
    				});
    			
    			return hashBlock("
    \n" + bq + "\n
    "); }); return text; } var _FormParagraphs = function(text) { // // Params: // $text - string to process with html

    tags // // Strip leading and trailing lines: text = text.replace(/^\n+/g,""); text = text.replace(/\n+$/g,""); var grafs = text.split(/\n{2,}/g); var grafsOut = new Array(); // // Wrap

    tags. // var end = grafs.length; for (var i=0; i= 0) { grafsOut.push(str); } else if (str.search(/\S/) >= 0) { str = _RunSpanGamut(str); str = str.replace(/^([ \t]*)/g,"

    "); str += "

    " grafsOut.push(str); } } // // Unhashify HTML blocks // end = grafsOut.length; for (var i=0; i= 0) { var blockText = g_html_blocks[RegExp.$1]; blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText); } } return grafsOut.join("\n\n"); } var _EncodeAmpsAndAngles = function(text) { // Smart processing for ampersands and angle brackets that need to be encoded. // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: // http://bumppo.net/projects/amputator/ text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&"); // Encode naked <'s text = text.replace(/<(?![a-z\/?\$!])/gi,"<"); return text; } var _EncodeBackslashEscapes = function(text) { // // Parameter: String. // Returns: The string, with after processing the following backslash // escape sequences. // // attacklab: The polite way to do this is with the new // escapeCharacters() function: // // text = escapeCharacters(text,"\\",true); // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); // // ...but we're sidestepping its use of the (slow) RegExp constructor // as an optimization for Firefox. This function gets called a LOT. text = text.replace(/\\(\\)/g,escapeCharacters_callback); text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback); return text; } var _DoAutoLinks = function(text) { text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"
    $1"); // Email addresses: /* text = text.replace(/ < (?:mailto:)? ( [-.\w]+ \@ [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ ) > /gi, _DoAutoLinks_callback()); */ text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, function(wholeMatch,m1) { return _EncodeEmailAddress( _UnescapeSpecialChars(m1) ); } ); return text; } var _EncodeEmailAddress = function(addr) { // // Input: an email address, e.g. "foo@example.com" // // Output: the email address as a mailto link, with each character // of the address encoded as either a decimal or hex entity, in // the hopes of foiling most address harvesting spam bots. E.g.: // // foo // @example.com // // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk // mailing list: // // attacklab: why can't javascript speak hex? function char2hex(ch) { var hexDigits = '0123456789ABCDEF'; var dec = ch.charCodeAt(0); return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15)); } var encode = [ function(ch){return "&#"+ch.charCodeAt(0)+";";}, function(ch){return "&#x"+char2hex(ch)+";";}, function(ch){return ch;} ]; addr = "mailto:" + addr; addr = addr.replace(/./g, function(ch) { if (ch == "@") { // this *must* be encoded. I insist. ch = encode[Math.floor(Math.random()*2)](ch); } else if (ch !=":") { // leave ':' alone (to spot mailto: later) var r = Math.random(); // roughly 10% raw, 45% hex, 45% dec ch = ( r > .9 ? encode[2](ch) : r > .45 ? encode[1](ch) : encode[0](ch) ); } return ch; }); addr = "" + addr + ""; addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part return addr; } var _UnescapeSpecialChars = function(text) { // // Swap back in all the special characters we've hidden. // text = text.replace(/~E(\d+)E/g, function(wholeMatch,m1) { var charCodeToReplace = parseInt(m1); return String.fromCharCode(charCodeToReplace); } ); return text; } var _Outdent = function(text) { // // Remove one level of line-leading tabs or spaces // // attacklab: hack around Konqueror 3.5.4 bug: // "----------bug".replace(/^-/g,"") == "bug" text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width // attacklab: clean up hack text = text.replace(/~0/g,"") return text; } var _Detab = function(text) { // attacklab: Detab's completely rewritten for speed. // In perl we could fix it by anchoring the regexp with \G. // In javascript we're less fortunate. // expand first n-1 tabs text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width // replace the nth with two sentinels text = text.replace(/\t/g,"~A~B"); // use the sentinel to anchor our regex so it doesn't explode text = text.replace(/~B(.+?)~A/g, function(wholeMatch,m1,m2) { var leadingText = m1; var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width // there *must* be a better way to do this: for (var i=0; i /dev/null` COMPILER = ${JS_ENGINE} ${BUILD_DIR}/uglify.js --unsafe POST_COMPILER = ${JS_ENGINE} ${BUILD_DIR}/post-compile.js SRC = ${SRC_DIR}/jsonselect.js DIST = ${DIST_DIR}/jsonselect.js DIST_MIN = ${DIST_DIR}/jsonselect.min.js all: hint project min tests @@echo "Project build complete." ${DIST_DIR}: @@mkdir -p ${DIST_DIR} project: ${DIST} ${DIST}: ${SRC} | ${DIST_DIR} @@echo "Building" ${DIST} @@echo ${SRC} @@cat ${SRC} > ${DIST}; min: project ${DIST_MIN} ${DIST_MIN}: ${DIST} @@if test ! -z ${JS_ENGINE}; then \ echo "Minifying Project" ${DIST_MIN}; \ ${COMPILER} ${DIST} > ${DIST_MIN}.tmp; \ ${POST_COMPILER} ${DIST_MIN}.tmp > ${DIST_MIN}; \ rm -f ${DIST_MIN}.tmp; \ else \ echo "You must have NodeJS installed in order to minify Project."; \ fi hint: @@if test ! -z ${JS_ENGINE}; then \ echo "Hinting Project"; \ ${JS_ENGINE} build/jshint-check.js; \ else \ echo "Nodejs is missing"; \ fi test/tests/README.md: @@cd .. && git submodule init @@cd .. && git submodule update tests: test/tests/README.md @@if test ! -z ${JS_ENGINE}; then \ echo "Testing Project"; \ ${JS_ENGINE} test/run.js; \ else \ echo "nodejs is missing"; \ fi clean: @@echo "Removing Distribution directory:" ${DIST_DIR} @@rm -rf ${DIST_DIR} .PHONY: all project hint min tests JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/build/000775 000000 000000 00000000000 11710614664 022116 5ustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/build/jshint-check.js000664 000000 000000 00000002060 11710614664 025024 0ustar00rootroot000000 000000 var JSHINT = require("./lib/jshint").JSHINT, print = require("sys").print, src = require("fs").readFileSync("jsonselect.js", "utf8"); JSHINT(src, { evil: true, forin: true, maxerr: 100 }); var ok = { // w.reason "Expected an identifier and instead saw 'undefined' (a reserved word).": true, "Use '===' to compare with 'null'.": true, "Use '!==' to compare with 'null'.": true, "Expected an assignment or function call and instead saw an expression.": true, "Expected a 'break' statement before 'case'.": true, "'e' is already defined.": true, // w.raw "Expected an identifier and instead saw \'{a}\' (a reserved word).": true }; var e = JSHINT.errors, found = 0, w; for ( var i = 0; i < e.length; i++ ) { w = e[i]; //console.log( w ); if ( !ok[ w.reason ] && !ok[ w.raw ] ) { found++; print( "\n " + found + ". Problem at line " + w.line + " character " + w.character + ": " + w.reason ); print( "\n" + w.evidence ); } } if ( found > 0 ) { print( "\n\n" + found + " Error(s) found.\n" ); } else { print( "JSHint check passed.\n" ); } JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/build/lib/000775 000000 000000 00000000000 11710614664 022664 5ustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/build/lib/parse-js.js000664 000000 000000 00000146441 11710614664 024760 0ustar00rootroot000000 000000 /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. This version is suitable for Node.js. With minimal changes (the exports stuff) it should work on any JS platform. This file contains the tokenizer/parser. It is a port to JavaScript of parse-js [1], a JavaScript parser library written in Common Lisp by Marijn Haverbeke. Thank you Marijn! [1] http://marijn.haverbeke.nl/parse-js/ Exported functions: - tokenizer(code) -- returns a function. Call the returned function to fetch the next token. - parse(code) -- returns an AST of the given JavaScript code. -------------------------------- (C) --------------------------------- Author: Mihai Bazon http://mihai.bazon.net/blog Distributed under the BSD license: Copyright 2010 (c) Mihai Bazon Based on parse-js (http://marijn.haverbeke.nl/parse-js/). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ /* -----[ Tokenizer (constants) ]----- */ var KEYWORDS = array_to_hash([ "break", "case", "catch", "const", "continue", "default", "delete", "do", "else", "finally", "for", "function", "if", "in", "instanceof", "new", "return", "switch", "throw", "try", "typeof", "var", "void", "while", "with" ]); var RESERVED_WORDS = array_to_hash([ "abstract", "boolean", "byte", "char", "class", "debugger", "double", "enum", "export", "extends", "final", "float", "goto", "implements", "import", "int", "interface", "long", "native", "package", "private", "protected", "public", "short", "static", "super", "synchronized", "throws", "transient", "volatile" ]); var KEYWORDS_BEFORE_EXPRESSION = array_to_hash([ "return", "new", "delete", "throw", "else", "case" ]); var KEYWORDS_ATOM = array_to_hash([ "false", "null", "true", "undefined" ]); var OPERATOR_CHARS = array_to_hash(characters("+-*&%=<>!?|~^")); var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; var RE_OCT_NUMBER = /^0[0-7]+$/; var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i; var OPERATORS = array_to_hash([ "in", "instanceof", "typeof", "new", "void", "delete", "++", "--", "+", "-", "!", "~", "&", "|", "^", "*", "/", "%", ">>", "<<", ">>>", "<", ">", "<=", ">=", "==", "===", "!=", "!==", "?", "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=", "&&", "||" ]); var WHITESPACE_CHARS = array_to_hash(characters(" \n\r\t\u200b")); var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:")); var PUNC_CHARS = array_to_hash(characters("[]{}(),;:")); var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy")); /* -----[ Tokenizer ]----- */ // regexps adapted from http://xregexp.com/plugins/#unicode var UNICODE = { letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"), non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"), space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"), connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]") }; function is_letter(ch) { return UNICODE.letter.test(ch); }; function is_digit(ch) { ch = ch.charCodeAt(0); return ch >= 48 && ch <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9 }; function is_alphanumeric_char(ch) { return is_digit(ch) || is_letter(ch); }; function is_unicode_combining_mark(ch) { return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch); }; function is_unicode_connector_punctuation(ch) { return UNICODE.connector_punctuation.test(ch); }; function is_identifier_start(ch) { return ch == "$" || ch == "_" || is_letter(ch); }; function is_identifier_char(ch) { return is_identifier_start(ch) || is_unicode_combining_mark(ch) || is_digit(ch) || is_unicode_connector_punctuation(ch) || ch == "\u200c" // zero-width non-joiner || ch == "\u200d" // zero-width joiner (in my ECMA-262 PDF, this is also 200c) ; }; function parse_js_number(num) { if (RE_HEX_NUMBER.test(num)) { return parseInt(num.substr(2), 16); } else if (RE_OCT_NUMBER.test(num)) { return parseInt(num.substr(1), 8); } else if (RE_DEC_NUMBER.test(num)) { return parseFloat(num); } }; function JS_Parse_Error(message, line, col, pos) { this.message = message; this.line = line; this.col = col; this.pos = pos; try { ({})(); } catch(ex) { this.stack = ex.stack; }; }; JS_Parse_Error.prototype.toString = function() { return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack; }; function js_error(message, line, col, pos) { throw new JS_Parse_Error(message, line, col, pos); }; function is_token(token, type, val) { return token.type == type && (val == null || token.value == val); }; var EX_EOF = {}; function tokenizer($TEXT) { var S = { text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''), pos : 0, tokpos : 0, line : 0, tokline : 0, col : 0, tokcol : 0, newline_before : false, regex_allowed : false, comments_before : [] }; function peek() { return S.text.charAt(S.pos); }; function next(signal_eof) { var ch = S.text.charAt(S.pos++); if (signal_eof && !ch) throw EX_EOF; if (ch == "\n") { S.newline_before = true; ++S.line; S.col = 0; } else { ++S.col; } return ch; }; function eof() { return !S.peek(); }; function find(what, signal_eof) { var pos = S.text.indexOf(what, S.pos); if (signal_eof && pos == -1) throw EX_EOF; return pos; }; function start_token() { S.tokline = S.line; S.tokcol = S.col; S.tokpos = S.pos; }; function token(type, value, is_comment) { S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) || (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) || (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value))); var ret = { type : type, value : value, line : S.tokline, col : S.tokcol, pos : S.tokpos, nlb : S.newline_before }; if (!is_comment) { ret.comments_before = S.comments_before; S.comments_before = []; } S.newline_before = false; return ret; }; function skip_whitespace() { while (HOP(WHITESPACE_CHARS, peek())) next(); }; function read_while(pred) { var ret = "", ch = peek(), i = 0; while (ch && pred(ch, i++)) { ret += next(); ch = peek(); } return ret; }; function parse_error(err) { js_error(err, S.tokline, S.tokcol, S.tokpos); }; function read_num(prefix) { var has_e = false, after_e = false, has_x = false, has_dot = prefix == "."; var num = read_while(function(ch, i){ if (ch == "x" || ch == "X") { if (has_x) return false; return has_x = true; } if (!has_x && (ch == "E" || ch == "e")) { if (has_e) return false; return has_e = after_e = true; } if (ch == "-") { if (after_e || (i == 0 && !prefix)) return true; return false; } if (ch == "+") return after_e; after_e = false; if (ch == ".") { if (!has_dot && !has_x) return has_dot = true; return false; } return is_alphanumeric_char(ch); }); if (prefix) num = prefix + num; var valid = parse_js_number(num); if (!isNaN(valid)) { return token("num", valid); } else { parse_error("Invalid syntax: " + num); } }; function read_escaped_char() { var ch = next(true); switch (ch) { case "n" : return "\n"; case "r" : return "\r"; case "t" : return "\t"; case "b" : return "\b"; case "v" : return "\v"; case "f" : return "\f"; case "0" : return "\0"; case "x" : return String.fromCharCode(hex_bytes(2)); case "u" : return String.fromCharCode(hex_bytes(4)); default : return ch; } }; function hex_bytes(n) { var num = 0; for (; n > 0; --n) { var digit = parseInt(next(true), 16); if (isNaN(digit)) parse_error("Invalid hex-character pattern in string"); num = (num << 4) | digit; } return num; }; function read_string() { return with_eof_error("Unterminated string constant", function(){ var quote = next(), ret = ""; for (;;) { var ch = next(true); if (ch == "\\") ch = read_escaped_char(); else if (ch == quote) break; ret += ch; } return token("string", ret); }); }; function read_line_comment() { next(); var i = find("\n"), ret; if (i == -1) { ret = S.text.substr(S.pos); S.pos = S.text.length; } else { ret = S.text.substring(S.pos, i); S.pos = i; } return token("comment1", ret, true); }; function read_multiline_comment() { next(); return with_eof_error("Unterminated multiline comment", function(){ var i = find("*/", true), text = S.text.substring(S.pos, i), tok = token("comment2", text, true); S.pos = i + 2; S.line += text.split("\n").length - 1; S.newline_before = text.indexOf("\n") >= 0; // https://github.com/mishoo/UglifyJS/issues/#issue/100 if (/^@cc_on/i.test(text)) { warn("WARNING: at line " + S.line); warn("*** Found \"conditional comment\": " + text); warn("*** UglifyJS DISCARDS ALL COMMENTS. This means your code might no longer work properly in Internet Explorer."); } return tok; }); }; function read_name() { var backslash = false, name = "", ch; while ((ch = peek()) != null) { if (!backslash) { if (ch == "\\") backslash = true, next(); else if (is_identifier_char(ch)) name += next(); else break; } else { if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX"); ch = read_escaped_char(); if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier"); name += ch; backslash = false; } } return name; }; function read_regexp() { return with_eof_error("Unterminated regular expression", function(){ var prev_backslash = false, regexp = "", ch, in_class = false; while ((ch = next(true))) if (prev_backslash) { regexp += "\\" + ch; prev_backslash = false; } else if (ch == "[") { in_class = true; regexp += ch; } else if (ch == "]" && in_class) { in_class = false; regexp += ch; } else if (ch == "/" && !in_class) { break; } else if (ch == "\\") { prev_backslash = true; } else { regexp += ch; } var mods = read_name(); return token("regexp", [ regexp, mods ]); }); }; function read_operator(prefix) { function grow(op) { if (!peek()) return op; var bigger = op + peek(); if (HOP(OPERATORS, bigger)) { next(); return grow(bigger); } else { return op; } }; return token("operator", grow(prefix || next())); }; function handle_slash() { next(); var regex_allowed = S.regex_allowed; switch (peek()) { case "/": S.comments_before.push(read_line_comment()); S.regex_allowed = regex_allowed; return next_token(); case "*": S.comments_before.push(read_multiline_comment()); S.regex_allowed = regex_allowed; return next_token(); } return S.regex_allowed ? read_regexp() : read_operator("/"); }; function handle_dot() { next(); return is_digit(peek()) ? read_num(".") : token("punc", "."); }; function read_word() { var word = read_name(); return !HOP(KEYWORDS, word) ? token("name", word) : HOP(OPERATORS, word) ? token("operator", word) : HOP(KEYWORDS_ATOM, word) ? token("atom", word) : token("keyword", word); }; function with_eof_error(eof_error, cont) { try { return cont(); } catch(ex) { if (ex === EX_EOF) parse_error(eof_error); else throw ex; } }; function next_token(force_regexp) { if (force_regexp) return read_regexp(); skip_whitespace(); start_token(); var ch = peek(); if (!ch) return token("eof"); if (is_digit(ch)) return read_num(); if (ch == '"' || ch == "'") return read_string(); if (HOP(PUNC_CHARS, ch)) return token("punc", next()); if (ch == ".") return handle_dot(); if (ch == "/") return handle_slash(); if (HOP(OPERATOR_CHARS, ch)) return read_operator(); if (ch == "\\" || is_identifier_start(ch)) return read_word(); parse_error("Unexpected character '" + ch + "'"); }; next_token.context = function(nc) { if (nc) S = nc; return S; }; return next_token; }; /* -----[ Parser (constants) ]----- */ var UNARY_PREFIX = array_to_hash([ "typeof", "void", "delete", "--", "++", "!", "~", "-", "+" ]); var UNARY_POSTFIX = array_to_hash([ "--", "++" ]); var ASSIGNMENT = (function(a, ret, i){ while (i < a.length) { ret[a[i]] = a[i].substr(0, a[i].length - 1); i++; } return ret; })( ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="], { "=": true }, 0 ); var PRECEDENCE = (function(a, ret){ for (var i = 0, n = 1; i < a.length; ++i, ++n) { var b = a[i]; for (var j = 0; j < b.length; ++j) { ret[b[j]] = n; } } return ret; })( [ ["||"], ["&&"], ["|"], ["^"], ["&"], ["==", "===", "!=", "!=="], ["<", ">", "<=", ">=", "in", "instanceof"], [">>", "<<", ">>>"], ["+", "-"], ["*", "/", "%"] ], {} ); var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]); /* -----[ Parser ]----- */ function NodeWithToken(str, start, end) { this.name = str; this.start = start; this.end = end; }; NodeWithToken.prototype.toString = function() { return this.name; }; function parse($TEXT, exigent_mode, embed_tokens) { var S = { input : typeof $TEXT == "string" ? tokenizer($TEXT, true) : $TEXT, token : null, prev : null, peeked : null, in_function : 0, in_loop : 0, labels : [] }; S.token = next(); function is(type, value) { return is_token(S.token, type, value); }; function peek() { return S.peeked || (S.peeked = S.input()); }; function next() { S.prev = S.token; if (S.peeked) { S.token = S.peeked; S.peeked = null; } else { S.token = S.input(); } return S.token; }; function prev() { return S.prev; }; function croak(msg, line, col, pos) { var ctx = S.input.context(); js_error(msg, line != null ? line : ctx.tokline, col != null ? col : ctx.tokcol, pos != null ? pos : ctx.tokpos); }; function token_error(token, msg) { croak(msg, token.line, token.col); }; function unexpected(token) { if (token == null) token = S.token; token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")"); }; function expect_token(type, val) { if (is(type, val)) { return next(); } token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type); }; function expect(punc) { return expect_token("punc", punc); }; function can_insert_semicolon() { return !exigent_mode && ( S.token.nlb || is("eof") || is("punc", "}") ); }; function semicolon() { if (is("punc", ";")) next(); else if (!can_insert_semicolon()) unexpected(); }; function as() { return slice(arguments); }; function parenthesised() { expect("("); var ex = expression(); expect(")"); return ex; }; function add_tokens(str, start, end) { return str instanceof NodeWithToken ? str : new NodeWithToken(str, start, end); }; var statement = embed_tokens ? function() { var start = S.token; var ast = $statement.apply(this, arguments); ast[0] = add_tokens(ast[0], start, prev()); return ast; } : $statement; function $statement() { if (is("operator", "/")) { S.peeked = null; S.token = S.input(true); // force regexp } switch (S.token.type) { case "num": case "string": case "regexp": case "operator": case "atom": return simple_statement(); case "name": return is_token(peek(), "punc", ":") ? labeled_statement(prog1(S.token.value, next, next)) : simple_statement(); case "punc": switch (S.token.value) { case "{": return as("block", block_()); case "[": case "(": return simple_statement(); case ";": next(); return as("block"); default: unexpected(); } case "keyword": switch (prog1(S.token.value, next)) { case "break": return break_cont("break"); case "continue": return break_cont("continue"); case "debugger": semicolon(); return as("debugger"); case "do": return (function(body){ expect_token("keyword", "while"); return as("do", prog1(parenthesised, semicolon), body); })(in_loop(statement)); case "for": return for_(); case "function": return function_(true); case "if": return if_(); case "return": if (S.in_function == 0) croak("'return' outside of function"); return as("return", is("punc", ";") ? (next(), null) : can_insert_semicolon() ? null : prog1(expression, semicolon)); case "switch": return as("switch", parenthesised(), switch_block_()); case "throw": return as("throw", prog1(expression, semicolon)); case "try": return try_(); case "var": return prog1(var_, semicolon); case "const": return prog1(const_, semicolon); case "while": return as("while", parenthesised(), in_loop(statement)); case "with": return as("with", parenthesised(), statement()); default: unexpected(); } } }; function labeled_statement(label) { S.labels.push(label); var start = S.token, stat = statement(); if (exigent_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0])) unexpected(start); S.labels.pop(); return as("label", label, stat); }; function simple_statement() { return as("stat", prog1(expression, semicolon)); }; function break_cont(type) { var name = is("name") ? S.token.value : null; if (name != null) { next(); if (!member(name, S.labels)) croak("Label " + name + " without matching loop or statement"); } else if (S.in_loop == 0) croak(type + " not inside a loop or switch"); semicolon(); return as(type, name); }; function for_() { expect("("); var init = null; if (!is("punc", ";")) { init = is("keyword", "var") ? (next(), var_(true)) : expression(true, true); if (is("operator", "in")) return for_in(init); } return regular_for(init); }; function regular_for(init) { expect(";"); var test = is("punc", ";") ? null : expression(); expect(";"); var step = is("punc", ")") ? null : expression(); expect(")"); return as("for", init, test, step, in_loop(statement)); }; function for_in(init) { var lhs = init[0] == "var" ? as("name", init[1][0]) : init; next(); var obj = expression(); expect(")"); return as("for-in", init, lhs, obj, in_loop(statement)); }; var function_ = embed_tokens ? function() { var start = prev(); var ast = $function_.apply(this, arguments); ast[0] = add_tokens(ast[0], start, prev()); return ast; } : $function_; function $function_(in_statement) { var name = is("name") ? prog1(S.token.value, next) : null; if (in_statement && !name) unexpected(); expect("("); return as(in_statement ? "defun" : "function", name, // arguments (function(first, a){ while (!is("punc", ")")) { if (first) first = false; else expect(","); if (!is("name")) unexpected(); a.push(S.token.value); next(); } next(); return a; })(true, []), // body (function(){ ++S.in_function; var loop = S.in_loop; S.in_loop = 0; var a = block_(); --S.in_function; S.in_loop = loop; return a; })()); }; function if_() { var cond = parenthesised(), body = statement(), belse; if (is("keyword", "else")) { next(); belse = statement(); } return as("if", cond, body, belse); }; function block_() { expect("{"); var a = []; while (!is("punc", "}")) { if (is("eof")) unexpected(); a.push(statement()); } next(); return a; }; var switch_block_ = curry(in_loop, function(){ expect("{"); var a = [], cur = null; while (!is("punc", "}")) { if (is("eof")) unexpected(); if (is("keyword", "case")) { next(); cur = []; a.push([ expression(), cur ]); expect(":"); } else if (is("keyword", "default")) { next(); expect(":"); cur = []; a.push([ null, cur ]); } else { if (!cur) unexpected(); cur.push(statement()); } } next(); return a; }); function try_() { var body = block_(), bcatch, bfinally; if (is("keyword", "catch")) { next(); expect("("); if (!is("name")) croak("Name expected"); var name = S.token.value; next(); expect(")"); bcatch = [ name, block_() ]; } if (is("keyword", "finally")) { next(); bfinally = block_(); } if (!bcatch && !bfinally) croak("Missing catch/finally blocks"); return as("try", body, bcatch, bfinally); }; function vardefs(no_in) { var a = []; for (;;) { if (!is("name")) unexpected(); var name = S.token.value; next(); if (is("operator", "=")) { next(); a.push([ name, expression(false, no_in) ]); } else { a.push([ name ]); } if (!is("punc", ",")) break; next(); } return a; }; function var_(no_in) { return as("var", vardefs(no_in)); }; function const_() { return as("const", vardefs()); }; function new_() { var newexp = expr_atom(false), args; if (is("punc", "(")) { next(); args = expr_list(")"); } else { args = []; } return subscripts(as("new", newexp, args), true); }; function expr_atom(allow_calls) { if (is("operator", "new")) { next(); return new_(); } if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) { return make_unary("unary-prefix", prog1(S.token.value, next), expr_atom(allow_calls)); } if (is("punc")) { switch (S.token.value) { case "(": next(); return subscripts(prog1(expression, curry(expect, ")")), allow_calls); case "[": next(); return subscripts(array_(), allow_calls); case "{": next(); return subscripts(object_(), allow_calls); } unexpected(); } if (is("keyword", "function")) { next(); return subscripts(function_(false), allow_calls); } if (HOP(ATOMIC_START_TOKEN, S.token.type)) { var atom = S.token.type == "regexp" ? as("regexp", S.token.value[0], S.token.value[1]) : as(S.token.type, S.token.value); return subscripts(prog1(atom, next), allow_calls); } unexpected(); }; function expr_list(closing, allow_trailing_comma, allow_empty) { var first = true, a = []; while (!is("punc", closing)) { if (first) first = false; else expect(","); if (allow_trailing_comma && is("punc", closing)) break; if (is("punc", ",") && allow_empty) { a.push([ "atom", "undefined" ]); } else { a.push(expression(false)); } } next(); return a; }; function array_() { return as("array", expr_list("]", !exigent_mode, true)); }; function object_() { var first = true, a = []; while (!is("punc", "}")) { if (first) first = false; else expect(","); if (!exigent_mode && is("punc", "}")) // allow trailing comma break; var type = S.token.type; var name = as_property_name(); if (type == "name" && (name == "get" || name == "set") && !is("punc", ":")) { a.push([ as_name(), function_(false), name ]); } else { expect(":"); a.push([ name, expression(false) ]); } } next(); return as("object", a); }; function as_property_name() { switch (S.token.type) { case "num": case "string": return prog1(S.token.value, next); } return as_name(); }; function as_name() { switch (S.token.type) { case "name": case "operator": case "keyword": case "atom": return prog1(S.token.value, next); default: unexpected(); } }; function subscripts(expr, allow_calls) { if (is("punc", ".")) { next(); return subscripts(as("dot", expr, as_name()), allow_calls); } if (is("punc", "[")) { next(); return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls); } if (allow_calls && is("punc", "(")) { next(); return subscripts(as("call", expr, expr_list(")")), true); } if (allow_calls && is("operator") && HOP(UNARY_POSTFIX, S.token.value)) { return prog1(curry(make_unary, "unary-postfix", S.token.value, expr), next); } return expr; }; function make_unary(tag, op, expr) { if ((op == "++" || op == "--") && !is_assignable(expr)) croak("Invalid use of " + op + " operator"); return as(tag, op, expr); }; function expr_op(left, min_prec, no_in) { var op = is("operator") ? S.token.value : null; if (op && op == "in" && no_in) op = null; var prec = op != null ? PRECEDENCE[op] : null; if (prec != null && prec > min_prec) { next(); var right = expr_op(expr_atom(true), prec, no_in); return expr_op(as("binary", op, left, right), min_prec, no_in); } return left; }; function expr_ops(no_in) { return expr_op(expr_atom(true), 0, no_in); }; function maybe_conditional(no_in) { var expr = expr_ops(no_in); if (is("operator", "?")) { next(); var yes = expression(false); expect(":"); return as("conditional", expr, yes, expression(false, no_in)); } return expr; }; function is_assignable(expr) { if (!exigent_mode) return true; switch (expr[0]) { case "dot": case "sub": case "new": case "call": return true; case "name": return expr[1] != "this"; } }; function maybe_assign(no_in) { var left = maybe_conditional(no_in), val = S.token.value; if (is("operator") && HOP(ASSIGNMENT, val)) { if (is_assignable(left)) { next(); return as("assign", ASSIGNMENT[val], left, maybe_assign(no_in)); } croak("Invalid assignment"); } return left; }; function expression(commas, no_in) { if (arguments.length == 0) commas = true; var expr = maybe_assign(no_in); if (commas && is("punc", ",")) { next(); return as("seq", expr, expression(true, no_in)); } return expr; }; function in_loop(cont) { try { ++S.in_loop; return cont(); } finally { --S.in_loop; } }; return as("toplevel", (function(a){ while (!is("eof")) a.push(statement()); return a; })([])); }; /* -----[ Utilities ]----- */ function curry(f) { var args = slice(arguments, 1); return function() { return f.apply(this, args.concat(slice(arguments))); }; }; function prog1(ret) { if (ret instanceof Function) ret = ret(); for (var i = 1, n = arguments.length; --n > 0; ++i) arguments[i](); return ret; }; function array_to_hash(a) { var ret = {}; for (var i = 0; i < a.length; ++i) ret[a[i]] = true; return ret; }; function slice(a, start) { return Array.prototype.slice.call(a, start == null ? 0 : start); }; function characters(str) { return str.split(""); }; function member(name, array) { for (var i = array.length; --i >= 0;) if (array[i] === name) return true; return false; }; function HOP(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }; var warn = function() {}; /* -----[ Exports ]----- */ exports.tokenizer = tokenizer; exports.parse = parse; exports.slice = slice; exports.curry = curry; exports.member = member; exports.array_to_hash = array_to_hash; exports.PRECEDENCE = PRECEDENCE; exports.KEYWORDS_ATOM = KEYWORDS_ATOM; exports.RESERVED_WORDS = RESERVED_WORDS; exports.KEYWORDS = KEYWORDS; exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN; exports.OPERATORS = OPERATORS; exports.is_alphanumeric_char = is_alphanumeric_char; exports.set_logger = function(logger) { warn = logger; }; JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/build/lib/process.js000664 000000 000000 00000211672 11710614664 024711 0ustar00rootroot000000 000000 /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. This version is suitable for Node.js. With minimal changes (the exports stuff) it should work on any JS platform. This file implements some AST processors. They work on data built by parse-js. Exported functions: - ast_mangle(ast, options) -- mangles the variable/function names in the AST. Returns an AST. - ast_squeeze(ast) -- employs various optimizations to make the final generated code even smaller. Returns an AST. - gen_code(ast, options) -- generates JS code from the AST. Pass true (or an object, see the code for some options) as second argument to get "pretty" (indented) code. -------------------------------- (C) --------------------------------- Author: Mihai Bazon http://mihai.bazon.net/blog Distributed under the BSD license: Copyright 2010 (c) Mihai Bazon Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ var jsp = require("./parse-js"), slice = jsp.slice, member = jsp.member, PRECEDENCE = jsp.PRECEDENCE, OPERATORS = jsp.OPERATORS; /* -----[ helper for AST traversal ]----- */ function ast_walker(ast) { function _vardefs(defs) { return [ this[0], MAP(defs, function(def){ var a = [ def[0] ]; if (def.length > 1) a[1] = walk(def[1]); return a; }) ]; }; var walkers = { "string": function(str) { return [ this[0], str ]; }, "num": function(num) { return [ this[0], num ]; }, "name": function(name) { return [ this[0], name ]; }, "toplevel": function(statements) { return [ this[0], MAP(statements, walk) ]; }, "block": function(statements) { var out = [ this[0] ]; if (statements != null) out.push(MAP(statements, walk)); return out; }, "var": _vardefs, "const": _vardefs, "try": function(t, c, f) { return [ this[0], MAP(t, walk), c != null ? [ c[0], MAP(c[1], walk) ] : null, f != null ? MAP(f, walk) : null ]; }, "throw": function(expr) { return [ this[0], walk(expr) ]; }, "new": function(ctor, args) { return [ this[0], walk(ctor), MAP(args, walk) ]; }, "switch": function(expr, body) { return [ this[0], walk(expr), MAP(body, function(branch){ return [ branch[0] ? walk(branch[0]) : null, MAP(branch[1], walk) ]; }) ]; }, "break": function(label) { return [ this[0], label ]; }, "continue": function(label) { return [ this[0], label ]; }, "conditional": function(cond, t, e) { return [ this[0], walk(cond), walk(t), walk(e) ]; }, "assign": function(op, lvalue, rvalue) { return [ this[0], op, walk(lvalue), walk(rvalue) ]; }, "dot": function(expr) { return [ this[0], walk(expr) ].concat(slice(arguments, 1)); }, "call": function(expr, args) { return [ this[0], walk(expr), MAP(args, walk) ]; }, "function": function(name, args, body) { return [ this[0], name, args.slice(), MAP(body, walk) ]; }, "defun": function(name, args, body) { return [ this[0], name, args.slice(), MAP(body, walk) ]; }, "if": function(conditional, t, e) { return [ this[0], walk(conditional), walk(t), walk(e) ]; }, "for": function(init, cond, step, block) { return [ this[0], walk(init), walk(cond), walk(step), walk(block) ]; }, "for-in": function(vvar, key, hash, block) { return [ this[0], walk(vvar), walk(key), walk(hash), walk(block) ]; }, "while": function(cond, block) { return [ this[0], walk(cond), walk(block) ]; }, "do": function(cond, block) { return [ this[0], walk(cond), walk(block) ]; }, "return": function(expr) { return [ this[0], walk(expr) ]; }, "binary": function(op, left, right) { return [ this[0], op, walk(left), walk(right) ]; }, "unary-prefix": function(op, expr) { return [ this[0], op, walk(expr) ]; }, "unary-postfix": function(op, expr) { return [ this[0], op, walk(expr) ]; }, "sub": function(expr, subscript) { return [ this[0], walk(expr), walk(subscript) ]; }, "object": function(props) { return [ this[0], MAP(props, function(p){ return p.length == 2 ? [ p[0], walk(p[1]) ] : [ p[0], walk(p[1]), p[2] ]; // get/set-ter }) ]; }, "regexp": function(rx, mods) { return [ this[0], rx, mods ]; }, "array": function(elements) { return [ this[0], MAP(elements, walk) ]; }, "stat": function(stat) { return [ this[0], walk(stat) ]; }, "seq": function() { return [ this[0] ].concat(MAP(slice(arguments), walk)); }, "label": function(name, block) { return [ this[0], name, walk(block) ]; }, "with": function(expr, block) { return [ this[0], walk(expr), walk(block) ]; }, "atom": function(name) { return [ this[0], name ]; } }; var user = {}; var stack = []; function walk(ast) { if (ast == null) return null; try { stack.push(ast); var type = ast[0]; var gen = user[type]; if (gen) { var ret = gen.apply(ast, ast.slice(1)); if (ret != null) return ret; } gen = walkers[type]; return gen.apply(ast, ast.slice(1)); } finally { stack.pop(); } }; function with_walkers(walkers, cont){ var save = {}, i; for (i in walkers) if (HOP(walkers, i)) { save[i] = user[i]; user[i] = walkers[i]; } var ret = cont(); for (i in save) if (HOP(save, i)) { if (!save[i]) delete user[i]; else user[i] = save[i]; } return ret; }; return { walk: walk, with_walkers: with_walkers, parent: function() { return stack[stack.length - 2]; // last one is current node }, stack: function() { return stack; } }; }; /* -----[ Scope and mangling ]----- */ function Scope(parent) { this.names = {}; // names defined in this scope this.mangled = {}; // mangled names (orig.name => mangled) this.rev_mangled = {}; // reverse lookup (mangled => orig.name) this.cname = -1; // current mangled name this.refs = {}; // names referenced from this scope this.uses_with = false; // will become TRUE if with() is detected in this or any subscopes this.uses_eval = false; // will become TRUE if eval() is detected in this or any subscopes this.parent = parent; // parent scope this.children = []; // sub-scopes if (parent) { this.level = parent.level + 1; parent.children.push(this); } else { this.level = 0; } }; var base54 = (function(){ var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_"; return function(num) { var ret = ""; do { ret = DIGITS.charAt(num % 54) + ret; num = Math.floor(num / 54); } while (num > 0); return ret; }; })(); Scope.prototype = { has: function(name) { for (var s = this; s; s = s.parent) if (HOP(s.names, name)) return s; }, has_mangled: function(mname) { for (var s = this; s; s = s.parent) if (HOP(s.rev_mangled, mname)) return s; }, toJSON: function() { return { names: this.names, uses_eval: this.uses_eval, uses_with: this.uses_with }; }, next_mangled: function() { // we must be careful that the new mangled name: // // 1. doesn't shadow a mangled name from a parent // scope, unless we don't reference the original // name from this scope OR from any sub-scopes! // This will get slow. // // 2. doesn't shadow an original name from a parent // scope, in the event that the name is not mangled // in the parent scope and we reference that name // here OR IN ANY SUBSCOPES! // // 3. doesn't shadow a name that is referenced but not // defined (possibly global defined elsewhere). for (;;) { var m = base54(++this.cname), prior; // case 1. prior = this.has_mangled(m); if (prior && this.refs[prior.rev_mangled[m]] === prior) continue; // case 2. prior = this.has(m); if (prior && prior !== this && this.refs[m] === prior && !prior.has_mangled(m)) continue; // case 3. if (HOP(this.refs, m) && this.refs[m] == null) continue; // I got "do" once. :-/ if (!is_identifier(m)) continue; return m; } }, get_mangled: function(name, newMangle) { if (this.uses_eval || this.uses_with) return name; // no mangle if eval or with is in use var s = this.has(name); if (!s) return name; // not in visible scope, no mangle if (HOP(s.mangled, name)) return s.mangled[name]; // already mangled in this scope if (!newMangle) return name; // not found and no mangling requested var m = s.next_mangled(); s.rev_mangled[m] = name; return s.mangled[name] = m; }, define: function(name) { if (name != null) return this.names[name] = name; } }; function ast_add_scope(ast) { var current_scope = null; var w = ast_walker(), walk = w.walk; var having_eval = []; function with_new_scope(cont) { current_scope = new Scope(current_scope); var ret = current_scope.body = cont(); ret.scope = current_scope; current_scope = current_scope.parent; return ret; }; function define(name) { return current_scope.define(name); }; function reference(name) { current_scope.refs[name] = true; }; function _lambda(name, args, body) { return [ this[0], this[0] == "defun" ? define(name) : name, args, with_new_scope(function(){ MAP(args, define); return MAP(body, walk); })]; }; return with_new_scope(function(){ // process AST var ret = w.with_walkers({ "function": _lambda, "defun": _lambda, "with": function(expr, block) { for (var s = current_scope; s; s = s.parent) s.uses_with = true; }, "var": function(defs) { MAP(defs, function(d){ define(d[0]) }); }, "const": function(defs) { MAP(defs, function(d){ define(d[0]) }); }, "try": function(t, c, f) { if (c != null) return [ this[0], MAP(t, walk), [ define(c[0]), MAP(c[1], walk) ], f != null ? MAP(f, walk) : null ]; }, "name": function(name) { if (name == "eval") having_eval.push(current_scope); reference(name); } }, function(){ return walk(ast); }); // the reason why we need an additional pass here is // that names can be used prior to their definition. // scopes where eval was detected and their parents // are marked with uses_eval, unless they define the // "eval" name. MAP(having_eval, function(scope){ if (!scope.has("eval")) while (scope) { scope.uses_eval = true; scope = scope.parent; } }); // for referenced names it might be useful to know // their origin scope. current_scope here is the // toplevel one. function fixrefs(scope, i) { // do children first; order shouldn't matter for (i = scope.children.length; --i >= 0;) fixrefs(scope.children[i]); for (i in scope.refs) if (HOP(scope.refs, i)) { // find origin scope and propagate the reference to origin for (var origin = scope.has(i), s = scope; s; s = s.parent) { s.refs[i] = origin; if (s === origin) break; } } }; fixrefs(current_scope); return ret; }); }; /* -----[ mangle names ]----- */ function ast_mangle(ast, options) { var w = ast_walker(), walk = w.walk, scope; options = options || {}; function get_mangled(name, newMangle) { if (!options.toplevel && !scope.parent) return name; // don't mangle toplevel if (options.except && member(name, options.except)) return name; return scope.get_mangled(name, newMangle); }; function _lambda(name, args, body) { if (name) name = get_mangled(name); body = with_scope(body.scope, function(){ args = MAP(args, function(name){ return get_mangled(name) }); return MAP(body, walk); }); return [ this[0], name, args, body ]; }; function with_scope(s, cont) { var _scope = scope; scope = s; for (var i in s.names) if (HOP(s.names, i)) { get_mangled(i, true); } var ret = cont(); ret.scope = s; scope = _scope; return ret; }; function _vardefs(defs) { return [ this[0], MAP(defs, function(d){ return [ get_mangled(d[0]), walk(d[1]) ]; }) ]; }; return w.with_walkers({ "function": _lambda, "defun": function() { // move function declarations to the top when // they are not in some block. var ast = _lambda.apply(this, arguments); switch (w.parent()[0]) { case "toplevel": case "function": case "defun": return MAP.at_top(ast); } return ast; }, "var": _vardefs, "const": _vardefs, "name": function(name) { return [ this[0], get_mangled(name) ]; }, "try": function(t, c, f) { return [ this[0], MAP(t, walk), c != null ? [ get_mangled(c[0]), MAP(c[1], walk) ] : null, f != null ? MAP(f, walk) : null ]; }, "toplevel": function(body) { var self = this; return with_scope(self.scope, function(){ return [ self[0], MAP(body, walk) ]; }); } }, function() { return walk(ast_add_scope(ast)); }); }; /* -----[ - compress foo["bar"] into foo.bar, - remove block brackets {} where possible - join consecutive var declarations - various optimizations for IFs: - if (cond) foo(); else bar(); ==> cond?foo():bar(); - if (cond) foo(); ==> cond&&foo(); - if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); // also for throw - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()} ]----- */ var warn = function(){}; function best_of(ast1, ast2) { return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1; }; function last_stat(b) { if (b[0] == "block" && b[1] && b[1].length > 0) return b[1][b[1].length - 1]; return b; } function aborts(t) { if (t) { t = last_stat(t); if (t[0] == "return" || t[0] == "break" || t[0] == "continue" || t[0] == "throw") return true; } }; function boolean_expr(expr) { return ( (expr[0] == "unary-prefix" && member(expr[1], [ "!", "delete" ])) || (expr[0] == "binary" && member(expr[1], [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ])) || (expr[0] == "binary" && member(expr[1], [ "&&", "||" ]) && boolean_expr(expr[2]) && boolean_expr(expr[3])) || (expr[0] == "conditional" && boolean_expr(expr[2]) && boolean_expr(expr[3])) || (expr[0] == "assign" && expr[1] === true && boolean_expr(expr[3])) || (expr[0] == "seq" && boolean_expr(expr[expr.length - 1])) ); }; function make_conditional(c, t, e) { if (c[0] == "unary-prefix" && c[1] == "!") { return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ]; } else { return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ]; } }; function empty(b) { return !b || (b[0] == "block" && (!b[1] || b[1].length == 0)); }; function is_string(node) { return (node[0] == "string" || node[0] == "unary-prefix" && node[1] == "typeof" || node[0] == "binary" && node[1] == "+" && (is_string(node[2]) || is_string(node[3]))); }; var when_constant = (function(){ var $NOT_CONSTANT = {}; // this can only evaluate constant expressions. If it finds anything // not constant, it throws $NOT_CONSTANT. function evaluate(expr) { switch (expr[0]) { case "string": case "num": return expr[1]; case "name": case "atom": switch (expr[1]) { case "true": return true; case "false": return false; } break; case "unary-prefix": switch (expr[1]) { case "!": return !evaluate(expr[2]); case "typeof": return typeof evaluate(expr[2]); case "~": return ~evaluate(expr[2]); case "-": return -evaluate(expr[2]); case "+": return +evaluate(expr[2]); } break; case "binary": var left = expr[2], right = expr[3]; switch (expr[1]) { case "&&" : return evaluate(left) && evaluate(right); case "||" : return evaluate(left) || evaluate(right); case "|" : return evaluate(left) | evaluate(right); case "&" : return evaluate(left) & evaluate(right); case "^" : return evaluate(left) ^ evaluate(right); case "+" : return evaluate(left) + evaluate(right); case "*" : return evaluate(left) * evaluate(right); case "/" : return evaluate(left) / evaluate(right); case "-" : return evaluate(left) - evaluate(right); case "<<" : return evaluate(left) << evaluate(right); case ">>" : return evaluate(left) >> evaluate(right); case ">>>" : return evaluate(left) >>> evaluate(right); case "==" : return evaluate(left) == evaluate(right); case "===" : return evaluate(left) === evaluate(right); case "!=" : return evaluate(left) != evaluate(right); case "!==" : return evaluate(left) !== evaluate(right); case "<" : return evaluate(left) < evaluate(right); case "<=" : return evaluate(left) <= evaluate(right); case ">" : return evaluate(left) > evaluate(right); case ">=" : return evaluate(left) >= evaluate(right); case "in" : return evaluate(left) in evaluate(right); case "instanceof" : return evaluate(left) instanceof evaluate(right); } } throw $NOT_CONSTANT; }; return function(expr, yes, no) { try { var val = evaluate(expr), ast; switch (typeof val) { case "string": ast = [ "string", val ]; break; case "number": ast = [ "num", val ]; break; case "boolean": ast = [ "name", String(val) ]; break; default: throw new Error("Can't handle constant of type: " + (typeof val)); } return yes.call(expr, ast, val); } catch(ex) { if (ex === $NOT_CONSTANT) { if (expr[0] == "binary" && (expr[1] == "===" || expr[1] == "!==") && ((is_string(expr[2]) && is_string(expr[3])) || (boolean_expr(expr[2]) && boolean_expr(expr[3])))) { expr[1] = expr[1].substr(0, 2); } return no ? no.call(expr, expr) : null; } else throw ex; } }; })(); function warn_unreachable(ast) { if (!empty(ast)) warn("Dropping unreachable code: " + gen_code(ast, true)); }; function ast_squeeze(ast, options) { options = defaults(options, { make_seqs : true, dead_code : true, keep_comps : true, no_warnings : false }); var w = ast_walker(), walk = w.walk, scope; function negate(c) { var not_c = [ "unary-prefix", "!", c ]; switch (c[0]) { case "unary-prefix": return c[1] == "!" && boolean_expr(c[2]) ? c[2] : not_c; case "seq": c = slice(c); c[c.length - 1] = negate(c[c.length - 1]); return c; case "conditional": return best_of(not_c, [ "conditional", c[1], negate(c[2]), negate(c[3]) ]); case "binary": var op = c[1], left = c[2], right = c[3]; if (!options.keep_comps) switch (op) { case "<=" : return [ "binary", ">", left, right ]; case "<" : return [ "binary", ">=", left, right ]; case ">=" : return [ "binary", "<", left, right ]; case ">" : return [ "binary", "<=", left, right ]; } switch (op) { case "==" : return [ "binary", "!=", left, right ]; case "!=" : return [ "binary", "==", left, right ]; case "===" : return [ "binary", "!==", left, right ]; case "!==" : return [ "binary", "===", left, right ]; case "&&" : return best_of(not_c, [ "binary", "||", negate(left), negate(right) ]); case "||" : return best_of(not_c, [ "binary", "&&", negate(left), negate(right) ]); } break; } return not_c; }; function with_scope(s, cont) { var _scope = scope; scope = s; var ret = cont(); ret.scope = s; scope = _scope; return ret; }; function rmblock(block) { if (block != null && block[0] == "block" && block[1]) { if (block[1].length == 1) block = block[1][0]; else if (block[1].length == 0) block = [ "block" ]; } return block; }; function _lambda(name, args, body) { return [ this[0], name, args, with_scope(body.scope, function(){ return tighten(MAP(body, walk), "lambda"); }) ]; }; // we get here for blocks that have been already transformed. // this function does a few things: // 1. discard useless blocks // 2. join consecutive var declarations // 3. remove obviously dead code // 4. transform consecutive statements using the comma operator // 5. if block_type == "lambda" and it detects constructs like if(foo) return ... - rewrite like if (!foo) { ... } function tighten(statements, block_type) { statements = statements.reduce(function(a, stat){ if (stat[0] == "block") { if (stat[1]) { a.push.apply(a, stat[1]); } } else { a.push(stat); } return a; }, []); statements = (function(a, prev){ statements.forEach(function(cur){ if (prev && ((cur[0] == "var" && prev[0] == "var") || (cur[0] == "const" && prev[0] == "const"))) { prev[1] = prev[1].concat(cur[1]); } else { a.push(cur); prev = cur; } }); return a; })([]); if (options.dead_code) statements = (function(a, has_quit){ statements.forEach(function(st){ if (has_quit) { if (member(st[0], [ "function", "defun" , "var", "const" ])) { a.push(st); } else if (!options.no_warnings) warn_unreachable(st); } else { a.push(st); if (member(st[0], [ "return", "throw", "break", "continue" ])) has_quit = true; } }); return a; })([]); if (options.make_seqs) statements = (function(a, prev) { statements.forEach(function(cur){ if (prev && prev[0] == "stat" && cur[0] == "stat") { prev[1] = [ "seq", prev[1], cur[1] ]; } else { a.push(cur); prev = cur; } }); return a; })([]); if (block_type == "lambda") statements = (function(i, a, stat){ while (i < statements.length) { stat = statements[i++]; if (stat[0] == "if" && !stat[3]) { if (stat[2][0] == "return" && stat[2][1] == null) { a.push(make_if(negate(stat[1]), [ "block", statements.slice(i) ])); break; } var last = last_stat(stat[2]); if (last[0] == "return" && last[1] == null) { a.push(make_if(stat[1], [ "block", stat[2][1].slice(0, -1) ], [ "block", statements.slice(i) ])); break; } } a.push(stat); } return a; })(0, []); return statements; }; function make_if(c, t, e) { return when_constant(c, function(ast, val){ if (val) { warn_unreachable(e); return t; } else { warn_unreachable(t); return e; } }, function() { return make_real_if(c, t, e); }); }; function make_real_if(c, t, e) { c = walk(c); t = walk(t); e = walk(e); if (empty(t)) { c = negate(c); t = e; e = null; } else if (empty(e)) { e = null; } else { // if we have both else and then, maybe it makes sense to switch them? (function(){ var a = gen_code(c); var n = negate(c); var b = gen_code(n); if (b.length < a.length) { var tmp = t; t = e; e = tmp; c = n; } })(); } if (empty(e) && empty(t)) return [ "stat", c ]; var ret = [ "if", c, t, e ]; if (t[0] == "if" && empty(t[3]) && empty(e)) { ret = best_of(ret, walk([ "if", [ "binary", "&&", c, t[1] ], t[2] ])); } else if (t[0] == "stat") { if (e) { if (e[0] == "stat") { ret = best_of(ret, [ "stat", make_conditional(c, t[1], e[1]) ]); } } else { ret = best_of(ret, [ "stat", make_conditional(c, t[1]) ]); } } else if (e && t[0] == e[0] && (t[0] == "return" || t[0] == "throw") && t[1] && e[1]) { ret = best_of(ret, [ t[0], make_conditional(c, t[1], e[1] ) ]); } else if (e && aborts(t)) { ret = [ [ "if", c, t ] ]; if (e[0] == "block") { if (e[1]) ret = ret.concat(e[1]); } else { ret.push(e); } ret = walk([ "block", ret ]); } else if (t && aborts(e)) { ret = [ [ "if", negate(c), e ] ]; if (t[0] == "block") { if (t[1]) ret = ret.concat(t[1]); } else { ret.push(t); } ret = walk([ "block", ret ]); } return ret; }; function _do_while(cond, body) { return when_constant(cond, function(cond, val){ if (!val) { warn_unreachable(body); return [ "block" ]; } else { return [ "for", null, null, null, walk(body) ]; } }); }; return w.with_walkers({ "sub": function(expr, subscript) { if (subscript[0] == "string") { var name = subscript[1]; if (is_identifier(name)) return [ "dot", walk(expr), name ]; else if (/^[1-9][0-9]*$/.test(name) || name === "0") return [ "sub", walk(expr), [ "num", parseInt(name, 10) ] ]; } }, "if": make_if, "toplevel": function(body) { return [ "toplevel", with_scope(this.scope, function(){ return tighten(MAP(body, walk)); }) ]; }, "switch": function(expr, body) { var last = body.length - 1; return [ "switch", walk(expr), MAP(body, function(branch, i){ var block = tighten(MAP(branch[1], walk)); if (i == last && block.length > 0) { var node = block[block.length - 1]; if (node[0] == "break" && !node[1]) block.pop(); } return [ branch[0] ? walk(branch[0]) : null, block ]; }) ]; }, "function": function() { var ret = _lambda.apply(this, arguments); if (ret[1] && !HOP(scope.refs, ret[1])) { ret[1] = null; } return ret; }, "defun": _lambda, "block": function(body) { if (body) return rmblock([ "block", tighten(MAP(body, walk)) ]); }, "binary": function(op, left, right) { return when_constant([ "binary", op, walk(left), walk(right) ], function yes(c){ return best_of(walk(c), this); }, function no() { return this; }); }, "conditional": function(c, t, e) { return make_conditional(walk(c), walk(t), walk(e)); }, "try": function(t, c, f) { return [ "try", tighten(MAP(t, walk)), c != null ? [ c[0], tighten(MAP(c[1], walk)) ] : null, f != null ? tighten(MAP(f, walk)) : null ]; }, "unary-prefix": function(op, expr) { expr = walk(expr); var ret = [ "unary-prefix", op, expr ]; if (op == "!") ret = best_of(ret, negate(expr)); return when_constant(ret, function(ast, val){ return walk(ast); // it's either true or false, so minifies to !0 or !1 }, function() { return ret }); }, "name": function(name) { switch (name) { case "true": return [ "unary-prefix", "!", [ "num", 0 ]]; case "false": return [ "unary-prefix", "!", [ "num", 1 ]]; } }, "new": function(ctor, args) { if (ctor[0] == "name" && ctor[1] == "Array" && !scope.has("Array")) { if (args.length != 1) { return [ "array", args ]; } else { return [ "call", [ "name", "Array" ], args ]; } } }, "call": function(expr, args) { if (expr[0] == "name" && expr[1] == "Array" && args.length != 1 && !scope.has("Array")) { return [ "array", args ]; } }, "while": _do_while, "do": _do_while }, function() { return walk(ast_add_scope(ast)); }); }; /* -----[ re-generate code from the AST ]----- */ var DOT_CALL_NO_PARENS = jsp.array_to_hash([ "name", "array", "object", "string", "dot", "sub", "call", "regexp" ]); function make_string(str, ascii_only) { var dq = 0, sq = 0; str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029]/g, function(s){ switch (s) { case "\\": return "\\\\"; case "\b": return "\\b"; case "\f": return "\\f"; case "\n": return "\\n"; case "\r": return "\\r"; case "\t": return "\\t"; case "\u2028": return "\\u2028"; case "\u2029": return "\\u2029"; case '"': ++dq; return '"'; case "'": ++sq; return "'"; } return s; }); if (ascii_only) str = to_ascii(str); if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'"; else return '"' + str.replace(/\x22/g, '\\"') + '"'; }; function to_ascii(str) { return str.replace(/[\u0080-\uffff]/g, function(ch) { var code = ch.charCodeAt(0).toString(16); while (code.length < 4) code = "0" + code; return "\\u" + code; }); }; function gen_code(ast, options) { options = defaults(options, { indent_start : 0, indent_level : 4, quote_keys : false, space_colon : false, beautify : false, ascii_only : false }); var beautify = !!options.beautify; var indentation = 0, newline = beautify ? "\n" : "", space = beautify ? " " : ""; function encode_string(str) { return make_string(str, options.ascii_only); }; function make_name(name) { name = name.toString(); if (options.ascii_only) name = to_ascii(name); return name; }; function indent(line) { if (line == null) line = ""; if (beautify) line = repeat_string(" ", options.indent_start + indentation * options.indent_level) + line; return line; }; function with_indent(cont, incr) { if (incr == null) incr = 1; indentation += incr; try { return cont.apply(null, slice(arguments, 1)); } finally { indentation -= incr; } }; function add_spaces(a) { if (beautify) return a.join(" "); var b = []; for (var i = 0; i < a.length; ++i) { var next = a[i + 1]; b.push(a[i]); if (next && ((/[a-z0-9_\x24]$/i.test(a[i].toString()) && /^[a-z0-9_\x24]/i.test(next.toString())) || (/[\+\-]$/.test(a[i].toString()) && /^[\+\-]/.test(next.toString())))) { b.push(" "); } } return b.join(""); }; function add_commas(a) { return a.join("," + space); }; function parenthesize(expr) { var gen = make(expr); for (var i = 1; i < arguments.length; ++i) { var el = arguments[i]; if ((el instanceof Function && el(expr)) || expr[0] == el) return "(" + gen + ")"; } return gen; }; function best_of(a) { if (a.length == 1) { return a[0]; } if (a.length == 2) { var b = a[1]; a = a[0]; return a.length <= b.length ? a : b; } return best_of([ a[0], best_of(a.slice(1)) ]); }; function needs_parens(expr) { if (expr[0] == "function" || expr[0] == "object") { // dot/call on a literal function requires the // function literal itself to be parenthesized // only if it's the first "thing" in a // statement. This means that the parent is // "stat", but it could also be a "seq" and // we're the first in this "seq" and the // parent is "stat", and so on. Messy stuff, // but it worths the trouble. var a = slice($stack), self = a.pop(), p = a.pop(); while (p) { if (p[0] == "stat") return true; if (((p[0] == "seq" || p[0] == "call" || p[0] == "dot" || p[0] == "sub" || p[0] == "conditional") && p[1] === self) || ((p[0] == "binary" || p[0] == "assign" || p[0] == "unary-postfix") && p[2] === self)) { self = p; p = a.pop(); } else { return false; } } } return !HOP(DOT_CALL_NO_PARENS, expr[0]); }; function make_num(num) { var str = num.toString(10), a = [ str.replace(/^0\./, ".") ], m; if (Math.floor(num) === num) { a.push("0x" + num.toString(16).toLowerCase(), // probably pointless "0" + num.toString(8)); // same. if ((m = /^(.*?)(0+)$/.exec(num))) { a.push(m[1] + "e" + m[2].length); } } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) { a.push(m[2] + "e-" + (m[1].length + m[2].length), str.substr(str.indexOf("."))); } return best_of(a); }; var generators = { "string": encode_string, "num": make_num, "name": make_name, "toplevel": function(statements) { return make_block_statements(statements) .join(newline + newline); }, "block": make_block, "var": function(defs) { return "var " + add_commas(MAP(defs, make_1vardef)) + ";"; }, "const": function(defs) { return "const " + add_commas(MAP(defs, make_1vardef)) + ";"; }, "try": function(tr, ca, fi) { var out = [ "try", make_block(tr) ]; if (ca) out.push("catch", "(" + ca[0] + ")", make_block(ca[1])); if (fi) out.push("finally", make_block(fi)); return add_spaces(out); }, "throw": function(expr) { return add_spaces([ "throw", make(expr) ]) + ";"; }, "new": function(ctor, args) { args = args.length > 0 ? "(" + add_commas(MAP(args, make)) + ")" : ""; return add_spaces([ "new", parenthesize(ctor, "seq", "binary", "conditional", "assign", function(expr){ var w = ast_walker(), has_call = {}; try { w.with_walkers({ "call": function() { throw has_call }, "function": function() { return this } }, function(){ w.walk(expr); }); } catch(ex) { if (ex === has_call) return true; throw ex; } }) + args ]); }, "switch": function(expr, body) { return add_spaces([ "switch", "(" + make(expr) + ")", make_switch_block(body) ]); }, "break": function(label) { var out = "break"; if (label != null) out += " " + make_name(label); return out + ";"; }, "continue": function(label) { var out = "continue"; if (label != null) out += " " + make_name(label); return out + ";"; }, "conditional": function(co, th, el) { return add_spaces([ parenthesize(co, "assign", "seq", "conditional"), "?", parenthesize(th, "seq"), ":", parenthesize(el, "seq") ]); }, "assign": function(op, lvalue, rvalue) { if (op && op !== true) op += "="; else op = "="; return add_spaces([ make(lvalue), op, parenthesize(rvalue, "seq") ]); }, "dot": function(expr) { var out = make(expr), i = 1; if (expr[0] == "num") { if (!/\./.test(expr[1])) out += "."; } else if (needs_parens(expr)) out = "(" + out + ")"; while (i < arguments.length) out += "." + make_name(arguments[i++]); return out; }, "call": function(func, args) { var f = make(func); if (needs_parens(func)) f = "(" + f + ")"; return f + "(" + add_commas(MAP(args, function(expr){ return parenthesize(expr, "seq"); })) + ")"; }, "function": make_function, "defun": make_function, "if": function(co, th, el) { var out = [ "if", "(" + make(co) + ")", el ? make_then(th) : make(th) ]; if (el) { out.push("else", make(el)); } return add_spaces(out); }, "for": function(init, cond, step, block) { var out = [ "for" ]; init = (init != null ? make(init) : "").replace(/;*\s*$/, ";" + space); cond = (cond != null ? make(cond) : "").replace(/;*\s*$/, ";" + space); step = (step != null ? make(step) : "").replace(/;*\s*$/, ""); var args = init + cond + step; if (args == "; ; ") args = ";;"; out.push("(" + args + ")", make(block)); return add_spaces(out); }, "for-in": function(vvar, key, hash, block) { return add_spaces([ "for", "(" + (vvar ? make(vvar).replace(/;+$/, "") : make(key)), "in", make(hash) + ")", make(block) ]); }, "while": function(condition, block) { return add_spaces([ "while", "(" + make(condition) + ")", make(block) ]); }, "do": function(condition, block) { return add_spaces([ "do", make(block), "while", "(" + make(condition) + ")" ]) + ";"; }, "return": function(expr) { var out = [ "return" ]; if (expr != null) out.push(make(expr)); return add_spaces(out) + ";"; }, "binary": function(operator, lvalue, rvalue) { var left = make(lvalue), right = make(rvalue); // XXX: I'm pretty sure other cases will bite here. // we need to be smarter. // adding parens all the time is the safest bet. if (member(lvalue[0], [ "assign", "conditional", "seq" ]) || lvalue[0] == "binary" && PRECEDENCE[operator] > PRECEDENCE[lvalue[1]]) { left = "(" + left + ")"; } if (member(rvalue[0], [ "assign", "conditional", "seq" ]) || rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[rvalue[1]] && !(rvalue[1] == operator && member(operator, [ "&&", "||", "*" ]))) { right = "(" + right + ")"; } return add_spaces([ left, operator, right ]); }, "unary-prefix": function(operator, expr) { var val = make(expr); if (!(expr[0] == "num" || (expr[0] == "unary-prefix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr))) val = "(" + val + ")"; return operator + (jsp.is_alphanumeric_char(operator.charAt(0)) ? " " : "") + val; }, "unary-postfix": function(operator, expr) { var val = make(expr); if (!(expr[0] == "num" || (expr[0] == "unary-postfix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr))) val = "(" + val + ")"; return val + operator; }, "sub": function(expr, subscript) { var hash = make(expr); if (needs_parens(expr)) hash = "(" + hash + ")"; return hash + "[" + make(subscript) + "]"; }, "object": function(props) { if (props.length == 0) return "{}"; return "{" + newline + with_indent(function(){ return MAP(props, function(p){ if (p.length == 3) { // getter/setter. The name is in p[0], the arg.list in p[1][2], the // body in p[1][3] and type ("get" / "set") in p[2]. return indent(make_function(p[0], p[1][2], p[1][3], p[2])); } var key = p[0], val = make(p[1]); if (options.quote_keys) { key = encode_string(key); } else if ((typeof key == "number" || !beautify && +key + "" == key) && parseFloat(key) >= 0) { key = make_num(+key); } else if (!is_identifier(key)) { key = encode_string(key); } return indent(add_spaces(beautify && options.space_colon ? [ key, ":", val ] : [ key + ":", val ])); }).join("," + newline); }) + newline + indent("}"); }, "regexp": function(rx, mods) { return "/" + rx + "/" + mods; }, "array": function(elements) { if (elements.length == 0) return "[]"; return add_spaces([ "[", add_commas(MAP(elements, function(el){ if (!beautify && el[0] == "atom" && el[1] == "undefined") return ""; return parenthesize(el, "seq"); })), "]" ]); }, "stat": function(stmt) { return make(stmt).replace(/;*\s*$/, ";"); }, "seq": function() { return add_commas(MAP(slice(arguments), make)); }, "label": function(name, block) { return add_spaces([ make_name(name), ":", make(block) ]); }, "with": function(expr, block) { return add_spaces([ "with", "(" + make(expr) + ")", make(block) ]); }, "atom": function(name) { return make_name(name); } }; // The squeezer replaces "block"-s that contain only a single // statement with the statement itself; technically, the AST // is correct, but this can create problems when we output an // IF having an ELSE clause where the THEN clause ends in an // IF *without* an ELSE block (then the outer ELSE would refer // to the inner IF). This function checks for this case and // adds the block brackets if needed. function make_then(th) { if (th[0] == "do") { // https://github.com/mishoo/UglifyJS/issues/#issue/57 // IE croaks with "syntax error" on code like this: // if (foo) do ... while(cond); else ... // we need block brackets around do/while return make([ "block", [ th ]]); } var b = th; while (true) { var type = b[0]; if (type == "if") { if (!b[3]) // no else, we must add the block return make([ "block", [ th ]]); b = b[3]; } else if (type == "while" || type == "do") b = b[2]; else if (type == "for" || type == "for-in") b = b[4]; else break; } return make(th); }; function make_function(name, args, body, keyword) { var out = keyword || "function"; if (name) { out += " " + make_name(name); } out += "(" + add_commas(MAP(args, make_name)) + ")"; return add_spaces([ out, make_block(body) ]); }; function make_block_statements(statements) { for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) { var stat = statements[i]; var code = make(stat); if (code != ";") { if (!beautify && i == last) { if ((stat[0] == "while" && empty(stat[2])) || (member(stat[0], [ "for", "for-in"] ) && empty(stat[4])) || (stat[0] == "if" && empty(stat[2]) && !stat[3]) || (stat[0] == "if" && stat[3] && empty(stat[3]))) { code = code.replace(/;*\s*$/, ";"); } else { code = code.replace(/;+\s*$/, ""); } } a.push(code); } } return MAP(a, indent); }; function make_switch_block(body) { var n = body.length; if (n == 0) return "{}"; return "{" + newline + MAP(body, function(branch, i){ var has_body = branch[1].length > 0, code = with_indent(function(){ return indent(branch[0] ? add_spaces([ "case", make(branch[0]) + ":" ]) : "default:"); }, 0.5) + (has_body ? newline + with_indent(function(){ return make_block_statements(branch[1]).join(newline); }) : ""); if (!beautify && has_body && i < n - 1) code += ";"; return code; }).join(newline) + newline + indent("}"); }; function make_block(statements) { if (!statements) return ";"; if (statements.length == 0) return "{}"; return "{" + newline + with_indent(function(){ return make_block_statements(statements).join(newline); }) + newline + indent("}"); }; function make_1vardef(def) { var name = def[0], val = def[1]; if (val != null) name = add_spaces([ make_name(name), "=", parenthesize(val, "seq") ]); return name; }; var $stack = []; function make(node) { var type = node[0]; var gen = generators[type]; if (!gen) throw new Error("Can't find generator for \"" + type + "\""); $stack.push(node); var ret = gen.apply(type, node.slice(1)); $stack.pop(); return ret; }; return make(ast); }; function split_lines(code, max_line_length) { var splits = [ 0 ]; jsp.parse(function(){ var next_token = jsp.tokenizer(code); var last_split = 0; var prev_token; function current_length(tok) { return tok.pos - last_split; }; function split_here(tok) { last_split = tok.pos; splits.push(last_split); }; function custom(){ var tok = next_token.apply(this, arguments); out: { if (prev_token) { if (prev_token.type == "keyword") break out; } if (current_length(tok) > max_line_length) { switch (tok.type) { case "keyword": case "atom": case "name": case "punc": split_here(tok); break out; } } } prev_token = tok; return tok; }; custom.context = function() { return next_token.context.apply(this, arguments); }; return custom; }()); return splits.map(function(pos, i){ return code.substring(pos, splits[i + 1] || code.length); }).join("\n"); }; /* -----[ Utilities ]----- */ function repeat_string(str, i) { if (i <= 0) return ""; if (i == 1) return str; var d = repeat_string(str, i >> 1); d += d; if (i & 1) d += str; return d; }; function defaults(args, defs) { var ret = {}; if (args === true) args = {}; for (var i in defs) if (HOP(defs, i)) { ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; } return ret; }; function is_identifier(name) { return /^[a-z_$][a-z0-9_$]*$/i.test(name) && name != "this" && !HOP(jsp.KEYWORDS_ATOM, name) && !HOP(jsp.RESERVED_WORDS, name) && !HOP(jsp.KEYWORDS, name); }; function HOP(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }; // some utilities var MAP; (function(){ MAP = function(a, f, o) { var ret = []; for (var i = 0; i < a.length; ++i) { var val = f.call(o, a[i], i); if (val instanceof AtTop) ret.unshift(val.v); else ret.push(val); } return ret; }; MAP.at_top = function(val) { return new AtTop(val) }; function AtTop(val) { this.v = val }; })(); /* -----[ Exports ]----- */ exports.ast_walker = ast_walker; exports.ast_mangle = ast_mangle; exports.ast_squeeze = ast_squeeze; exports.gen_code = gen_code; exports.ast_add_scope = ast_add_scope; exports.set_logger = function(logger) { warn = logger }; exports.make_string = make_string; exports.split_lines = split_lines; exports.MAP = MAP; // keep this last! exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more; JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/build/lib/squeeze-more.js000664 000000 000000 00000001346 11710614664 025647 0ustar00rootroot000000 000000 var jsp = require("./parse-js"), pro = require("./process"), slice = jsp.slice, member = jsp.member, PRECEDENCE = jsp.PRECEDENCE, OPERATORS = jsp.OPERATORS; function ast_squeeze_more(ast) { var w = pro.ast_walker(), walk = w.walk; return w.with_walkers({ "call": function(expr, args) { if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) { // foo.toString() ==> foo+"" return [ "binary", "+", expr[1], [ "string", "" ]]; } } }, function() { return walk(ast); }); }; exports.ast_squeeze_more = ast_squeeze_more; JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/build/post-compile.js000664 000000 000000 00000000361 11710614664 025067 0ustar00rootroot000000 000000 #!/usr/bin/env node var print = require("sys").print, src = require("fs").readFileSync(process.argv[2], "utf8"); // Previously done in sed but reimplemented here due to portability issues print(src.replace(/^(\s*\*\/)(.+)/m, "$1\n$2;")); JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/build/uglify.js000664 000000 000000 00000014261 11710614664 023757 0ustar00rootroot000000 000000 #! /usr/bin/env node // -*- js2 -*- global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util"); var fs = require("fs"), jsp = require("./lib/parse-js"), pro = require("./lib/process"); pro.set_logger(function(msg){ sys.debug(msg); }); var options = { ast: false, mangle: true, mangle_toplevel: false, squeeze: true, make_seqs: true, dead_code: true, beautify: false, verbose: false, show_copyright: true, out_same_file: false, extra: false, unsafe: false, // XXX: extra & unsafe? but maybe we don't want both, so.... beautify_options: { indent_level: 4, indent_start: 0, quote_keys: false, space_colon: false }, output: true // stdout }; var args = jsp.slice(process.argv, 2); var filename; out: while (args.length > 0) { var v = args.shift(); switch (v) { case "-b": case "--beautify": options.beautify = true; break; case "-i": case "--indent": options.beautify_options.indent_level = args.shift(); break; case "-q": case "--quote-keys": options.beautify_options.quote_keys = true; break; case "-mt": case "--mangle-toplevel": options.mangle_toplevel = true; break; case "--no-mangle": case "-nm": options.mangle = false; break; case "--no-squeeze": case "-ns": options.squeeze = false; break; case "--no-seqs": options.make_seqs = false; break; case "--no-dead-code": options.dead_code = false; break; case "--no-copyright": case "-nc": options.show_copyright = false; break; case "-o": case "--output": options.output = args.shift(); break; case "--overwrite": options.out_same_file = true; break; case "-v": case "--verbose": options.verbose = true; break; case "--ast": options.ast = true; break; case "--extra": options.extra = true; break; case "--unsafe": options.unsafe = true; break; default: filename = v; break out; } } if (filename) { fs.readFile(filename, "utf8", function(err, text){ if (err) { throw err; } output(squeeze_it(text)); }); } else { var stdin = process.openStdin(); stdin.setEncoding("utf8"); var text = ""; stdin.on("data", function(chunk){ text += chunk; }); stdin.on("end", function() { output(squeeze_it(text)); }); } function output(text) { var out; if (options.out_same_file && filename) options.output = filename; if (options.output === true) { out = process.stdout; } else { out = fs.createWriteStream(options.output, { flags: "w", encoding: "utf8", mode: 0644 }); } out.write(text); out.end(); }; // --------- main ends here. function show_copyright(comments) { var ret = ""; for (var i = 0; i < comments.length; ++i) { var c = comments[i]; if (c.type == "comment1") { ret += "//" + c.value + "\n"; } else { ret += "/*" + c.value + "*/"; } } return ret; }; function squeeze_it(code) { var result = ""; if (options.show_copyright) { var initial_comments = []; // keep first comment var tok = jsp.tokenizer(code, false), c; c = tok(); var prev = null; while (/^comment/.test(c.type) && (!prev || prev == c.type)) { initial_comments.push(c); prev = c.type; c = tok(); } result += show_copyright(initial_comments); } try { var ast = time_it("parse", function(){ return jsp.parse(code); }); if (options.mangle) ast = time_it("mangle", function(){ return pro.ast_mangle(ast, options.mangle_toplevel); }); if (options.squeeze) ast = time_it("squeeze", function(){ ast = pro.ast_squeeze(ast, { make_seqs : options.make_seqs, dead_code : options.dead_code, extra : options.extra }); if (options.unsafe) ast = pro.ast_squeeze_more(ast); return ast; }); if (options.ast) return sys.inspect(ast, null, null); result += time_it("generate", function(){ return pro.gen_code(ast, options.beautify && options.beautify_options) }); return result; } catch(ex) { sys.debug(ex.stack); sys.debug(sys.inspect(ex)); sys.debug(JSON.stringify(ex)); } }; function time_it(name, cont) { if (!options.verbose) return cont(); var t1 = new Date().getTime(); try { return cont(); } finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); } }; JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/jsonselect.js000664 000000 000000 00000050676 11710614664 023544 0ustar00rootroot000000 000000 /*! Copyright (c) 2011, Lloyd Hilaiel, ISC License */ /* * This is the JSONSelect reference implementation, in javascript. This * code is designed to run under node.js or in a browser. In the former * case, the "public API" is exposed as properties on the `export` object, * in the latter, as properties on `window.JSONSelect`. That API is thus: * * Selector formating and parameter escaping: * * Anywhere where a string selector is selected, it may be followed by an * optional array of values. When provided, they will be escaped and * inserted into the selector string properly escaped. i.e.: * * .match(':has(?)', [ 'foo' ], {}) * * would result in the seclector ':has("foo")' being matched against {}. * * This feature makes dynamically generated selectors more readable. * * .match(selector, [ values ], object) * * Parses and "compiles" the selector, then matches it against the object * argument. Matches are returned in an array. Throws an error when * there's a problem parsing the selector. * * .forEach(selector, [ values ], object, callback) * * Like match, but rather than returning an array, invokes the provided * callback once per match as the matches are discovered. * * .compile(selector, [ values ]) * * Parses the selector and compiles it to an internal form, and returns * an object which contains the compiled selector and has two properties: * `match` and `forEach`. These two functions work identically to the * above, except they do not take a selector as an argument and instead * use the compiled selector. * * For cases where a complex selector is repeatedly used, this method * should be faster as it will avoid recompiling the selector each time. */ (function(exports) { var // localize references toString = Object.prototype.toString; function jsonParse(str) { try { if(JSON && JSON.parse){ return JSON.parse(str); } return (new Function("return " + str))(); } catch(e) { te("ijs", e.message); } } // emitted error codes. var errorCodes = { "bop": "binary operator expected", "ee": "expression expected", "epex": "closing paren expected ')'", "ijs": "invalid json string", "mcp": "missing closing paren", "mepf": "malformed expression in pseudo-function", "mexp": "multiple expressions not allowed", "mpc": "multiple pseudo classes (:xxx) not allowed", "nmi": "multiple ids not allowed", "pex": "opening paren expected '('", "se": "selector expected", "sex": "string expected", "sra": "string required after '.'", "uc": "unrecognized char", "ucp": "unexpected closing paren", "ujs": "unclosed json string", "upc": "unrecognized pseudo class" }; // throw an error message function te(ec, context) { throw new Error(errorCodes[ec] + ( context && " in '" + context + "'")); } // THE LEXER var toks = { psc: 1, // pseudo class psf: 2, // pseudo class function typ: 3, // type str: 4, // string ide: 5 // identifiers (or "classes", stuff after a dot) }; // The primary lexing regular expression in jsonselect var pat = new RegExp( "^(?:" + // (1) whitespace "([\\r\\n\\t\\ ]+)|" + // (2) one-char ops "([~*,>\\)\\(])|" + // (3) types names "(string|boolean|null|array|object|number)|" + // (4) pseudo classes "(:(?:root|first-child|last-child|only-child))|" + // (5) pseudo functions "(:(?:nth-child|nth-last-child|has|expr|val|contains))|" + // (6) bogusly named pseudo something or others "(:\\w+)|" + // (7 & 8) identifiers and JSON strings "(?:(\\.)?(\\\"(?:[^\\\\\\\"]|\\\\[^\\\"])*\\\"))|" + // (8) bogus JSON strings missing a trailing quote "(\\\")|" + // (9) identifiers (unquoted) "\\.((?:[_a-zA-Z]|[^\\0-\\0177]|\\\\[^\\r\\n\\f0-9a-fA-F])(?:[_a-zA-Z0-9\\-]|[^\\u0000-\\u0177]|(?:\\\\[^\\r\\n\\f0-9a-fA-F]))*)" + ")" ); // A regular expression for matching "nth expressions" (see grammar, what :nth-child() eats) var nthPat = /^\s*\(\s*(?:([+\-]?)([0-9]*)n\s*(?:([+\-])\s*([0-9]))?|(odd|even)|([+\-]?[0-9]+))\s*\)/; function lex(str, off) { if (!off) off = 0; var m = pat.exec(str.substr(off)); if (!m) return undefined; off+=m[0].length; var a; if (m[1]) a = [off, " "]; else if (m[2]) a = [off, m[0]]; else if (m[3]) a = [off, toks.typ, m[0]]; else if (m[4]) a = [off, toks.psc, m[0]]; else if (m[5]) a = [off, toks.psf, m[0]]; else if (m[6]) te("upc", str); else if (m[8]) a = [off, m[7] ? toks.ide : toks.str, jsonParse(m[8])]; else if (m[9]) te("ujs", str); else if (m[10]) a = [off, toks.ide, m[10].replace(/\\([^\r\n\f0-9a-fA-F])/g,"$1")]; return a; } // THE EXPRESSION SUBSYSTEM var exprPat = new RegExp( // skip and don't capture leading whitespace "^\\s*(?:" + // (1) simple vals "(true|false|null)|" + // (2) numbers "(-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)|" + // (3) strings "(\"(?:[^\\]|\\[^\"])*\")|" + // (4) the 'x' value placeholder "(x)|" + // (5) binops "(&&|\\|\\||[\\$\\^<>!\\*]=|[=+\\-*/%<>])|" + // (6) parens "([\\(\\)])" + ")" ); function is(o, t) { return typeof o === t; } var operators = { '*': [ 9, function(lhs, rhs) { return lhs * rhs; } ], '/': [ 9, function(lhs, rhs) { return lhs / rhs; } ], '%': [ 9, function(lhs, rhs) { return lhs % rhs; } ], '+': [ 7, function(lhs, rhs) { return lhs + rhs; } ], '-': [ 7, function(lhs, rhs) { return lhs - rhs; } ], '<=': [ 5, function(lhs, rhs) { return is(lhs, 'number') && is(rhs, 'number') && lhs <= rhs; } ], '>=': [ 5, function(lhs, rhs) { return is(lhs, 'number') && is(rhs, 'number') && lhs >= rhs; } ], '$=': [ 5, function(lhs, rhs) { return is(lhs, 'string') && is(rhs, 'string') && lhs.lastIndexOf(rhs) === lhs.length - rhs.length; } ], '^=': [ 5, function(lhs, rhs) { return is(lhs, 'string') && is(rhs, 'string') && lhs.indexOf(rhs) === 0; } ], '*=': [ 5, function(lhs, rhs) { return is(lhs, 'string') && is(rhs, 'string') && lhs.indexOf(rhs) !== -1; } ], '>': [ 5, function(lhs, rhs) { return is(lhs, 'number') && is(rhs, 'number') && lhs > rhs; } ], '<': [ 5, function(lhs, rhs) { return is(lhs, 'number') && is(rhs, 'number') && lhs < rhs; } ], '=': [ 3, function(lhs, rhs) { return lhs === rhs; } ], '!=': [ 3, function(lhs, rhs) { return lhs !== rhs; } ], '&&': [ 2, function(lhs, rhs) { return lhs && rhs; } ], '||': [ 1, function(lhs, rhs) { return lhs || rhs; } ] }; function exprLex(str, off) { var v, m = exprPat.exec(str.substr(off)); if (m) { off += m[0].length; v = m[1] || m[2] || m[3] || m[5] || m[6]; if (m[1] || m[2] || m[3]) return [off, 0, jsonParse(v)]; else if (m[4]) return [off, 0, undefined]; return [off, v]; } } function exprParse2(str, off) { if (!off) off = 0; // first we expect a value or a '(' var l = exprLex(str, off), lhs; if (l && l[1] === '(') { lhs = exprParse2(str, l[0]); var p = exprLex(str, lhs[0]); if (!p || p[1] !== ')') te('epex', str); off = p[0]; lhs = [ '(', lhs[1] ]; } else if (!l || (l[1] && l[1] != 'x')) { te("ee", str + " - " + ( l[1] && l[1] )); } else { lhs = ((l[1] === 'x') ? undefined : l[2]); off = l[0]; } // now we expect a binary operator or a ')' var op = exprLex(str, off); if (!op || op[1] == ')') return [off, lhs]; else if (op[1] == 'x' || !op[1]) { te('bop', str + " - " + ( op[1] && op[1] )); } // tail recursion to fetch the rhs expression var rhs = exprParse2(str, op[0]); off = rhs[0]; rhs = rhs[1]; // and now precedence! how shall we put everything together? var v; if (typeof rhs !== 'object' || rhs[0] === '(' || operators[op[1]][0] < operators[rhs[1]][0] ) { v = [lhs, op[1], rhs]; } else { v = rhs; while (typeof rhs[0] === 'object' && rhs[0][0] != '(' && operators[op[1]][0] >= operators[rhs[0][1]][0]) { rhs = rhs[0]; } rhs[0] = [lhs, op[1], rhs[0]]; } return [off, v]; } function exprParse(str, off) { function deparen(v) { if (typeof v !== 'object' || v === null) return v; else if (v[0] === '(') return deparen(v[1]); else return [deparen(v[0]), v[1], deparen(v[2])]; } var e = exprParse2(str, off ? off : 0); return [e[0], deparen(e[1])]; } function exprEval(expr, x) { if (expr === undefined) return x; else if (expr === null || typeof expr !== 'object') { return expr; } var lhs = exprEval(expr[0], x), rhs = exprEval(expr[2], x); return operators[expr[1]][1](lhs, rhs); } // THE PARSER function parse(str, off, nested, hints) { if (!nested) hints = {}; var a = [], am, readParen; if (!off) off = 0; while (true) { var s = parse_selector(str, off, hints); a.push(s[1]); s = lex(str, off = s[0]); if (s && s[1] === " ") s = lex(str, off = s[0]); if (!s) break; // now we've parsed a selector, and have something else... if (s[1] === ">" || s[1] === "~") { if (s[1] === "~") hints.usesSiblingOp = true; a.push(s[1]); off = s[0]; } else if (s[1] === ",") { if (am === undefined) am = [ ",", a ]; else am.push(a); a = []; off = s[0]; } else if (s[1] === ")") { if (!nested) te("ucp", s[1]); readParen = 1; off = s[0]; break; } } if (nested && !readParen) te("mcp", str); if (am) am.push(a); var rv; if (!nested && hints.usesSiblingOp) { rv = normalize(am ? am : a); } else { rv = am ? am : a; } return [off, rv]; } function normalizeOne(sel) { var sels = [], s; for (var i = 0; i < sel.length; i++) { if (sel[i] === '~') { // `A ~ B` maps to `:has(:root > A) > B` // `Z A ~ B` maps to `Z :has(:root > A) > B, Z:has(:root > A) > B` // This first clause, takes care of the first case, and the first half of the latter case. if (i < 2 || sel[i-2] != '>') { s = sel.slice(0,i-1); s = s.concat([{has:[[{pc: ":root"}, ">", sel[i-1]]]}, ">"]); s = s.concat(sel.slice(i+1)); sels.push(s); } // here we take care of the second half of above: // (`Z A ~ B` maps to `Z :has(:root > A) > B, Z :has(:root > A) > B`) // and a new case: // Z > A ~ B maps to Z:has(:root > A) > B if (i > 1) { var at = sel[i-2] === '>' ? i-3 : i-2; s = sel.slice(0,at); var z = {}; for (var k in sel[at]) if (sel[at].hasOwnProperty(k)) z[k] = sel[at][k]; if (!z.has) z.has = []; z.has.push([{pc: ":root"}, ">", sel[i-1]]); s = s.concat(z, '>', sel.slice(i+1)); sels.push(s); } break; } } if (i == sel.length) return sel; return sels.length > 1 ? [','].concat(sels) : sels[0]; } function normalize(sels) { if (sels[0] === ',') { var r = [","]; for (var i = i; i < sels.length; i++) { var s = normalizeOne(s[i]); r = r.concat(s[0] === "," ? s.slice(1) : s); } return r; } else { return normalizeOne(sels); } } function parse_selector(str, off, hints) { var soff = off; var s = { }; var l = lex(str, off); // skip space if (l && l[1] === " ") { soff = off = l[0]; l = lex(str, off); } if (l && l[1] === toks.typ) { s.type = l[2]; l = lex(str, (off = l[0])); } else if (l && l[1] === "*") { // don't bother representing the universal sel, '*' in the // parse tree, cause it's the default l = lex(str, (off = l[0])); } // now support either an id or a pc while (true) { if (l === undefined) { break; } else if (l[1] === toks.ide) { if (s.id) te("nmi", l[1]); s.id = l[2]; } else if (l[1] === toks.psc) { if (s.pc || s.pf) te("mpc", l[1]); // collapse first-child and last-child into nth-child expressions if (l[2] === ":first-child") { s.pf = ":nth-child"; s.a = 0; s.b = 1; } else if (l[2] === ":last-child") { s.pf = ":nth-last-child"; s.a = 0; s.b = 1; } else { s.pc = l[2]; } } else if (l[1] === toks.psf) { if (l[2] === ":val" || l[2] === ":contains") { s.expr = [ undefined, l[2] === ":val" ? "=" : "*=", undefined]; // any amount of whitespace, followed by paren, string, paren l = lex(str, (off = l[0])); if (l && l[1] === " ") l = lex(str, off = l[0]); if (!l || l[1] !== "(") te("pex", str); l = lex(str, (off = l[0])); if (l && l[1] === " ") l = lex(str, off = l[0]); if (!l || l[1] !== toks.str) te("sex", str); s.expr[2] = l[2]; l = lex(str, (off = l[0])); if (l && l[1] === " ") l = lex(str, off = l[0]); if (!l || l[1] !== ")") te("epex", str); } else if (l[2] === ":has") { // any amount of whitespace, followed by paren l = lex(str, (off = l[0])); if (l && l[1] === " ") l = lex(str, off = l[0]); if (!l || l[1] !== "(") te("pex", str); var h = parse(str, l[0], true); l[0] = h[0]; if (!s.has) s.has = []; s.has.push(h[1]); } else if (l[2] === ":expr") { if (s.expr) te("mexp", str); var e = exprParse(str, l[0]); l[0] = e[0]; s.expr = e[1]; } else { if (s.pc || s.pf ) te("mpc", str); s.pf = l[2]; var m = nthPat.exec(str.substr(l[0])); if (!m) te("mepf", str); if (m[5]) { s.a = 2; s.b = (m[5] === "odd") ? 1 : 0; } else if (m[6]) { s.a = 0; s.b = parseInt(m[6], 10); } else { s.a = parseInt((m[1] ? m[1] : "+") + (m[2] ? m[2] : "1"),10); s.b = m[3] ? parseInt(m[3] + m[4],10) : 0; } l[0] += m[0].length; } } else { break; } l = lex(str, (off = l[0])); } // now if we didn't actually parse anything it's an error if (soff === off) te("se", str); return [off, s]; } // THE EVALUATOR function isArray(o) { return Array.isArray ? Array.isArray(o) : toString.call(o) === "[object Array]"; } function mytypeof(o) { if (o === null) return "null"; var to = typeof o; if (to === "object" && isArray(o)) to = "array"; return to; } function mn(node, sel, id, num, tot) { var sels = []; var cs = (sel[0] === ">") ? sel[1] : sel[0]; var m = true, mod; if (cs.type) m = m && (cs.type === mytypeof(node)); if (cs.id) m = m && (cs.id === id); if (m && cs.pf) { if (cs.pf === ":nth-last-child") num = tot - num; else num++; if (cs.a === 0) { m = cs.b === num; } else { mod = ((num - cs.b) % cs.a); m = (!mod && ((num*cs.a + cs.b) >= 0)); } } if (m && cs.has) { // perhaps we should augment forEach to handle a return value // that indicates "client cancels traversal"? var bail = function() { throw 42; }; for (var i = 0; i < cs.has.length; i++) { try { forEach(cs.has[i], node, bail); } catch (e) { if (e === 42) continue; } m = false; break; } } if (m && cs.expr) { m = exprEval(cs.expr, node); } // should we repeat this selector for descendants? if (sel[0] !== ">" && sel[0].pc !== ":root") sels.push(sel); if (m) { // is there a fragment that we should pass down? if (sel[0] === ">") { if (sel.length > 2) { m = false; sels.push(sel.slice(2)); } } else if (sel.length > 1) { m = false; sels.push(sel.slice(1)); } } return [m, sels]; } function forEach(sel, obj, fun, id, num, tot) { var a = (sel[0] === ",") ? sel.slice(1) : [sel], a0 = [], call = false, i = 0, j = 0, k, x; for (i = 0; i < a.length; i++) { x = mn(obj, a[i], id, num, tot); if (x[0]) { call = true; } for (j = 0; j < x[1].length; j++) { a0.push(x[1][j]); } } if (a0.length && typeof obj === "object") { if (a0.length >= 1) { a0.unshift(","); } if (isArray(obj)) { for (i = 0; i < obj.length; i++) { forEach(a0, obj[i], fun, undefined, i, obj.length); } } else { for (k in obj) { if (obj.hasOwnProperty(k)) { forEach(a0, obj[k], fun, k); } } } } if (call && fun) { fun(obj); } } function match(sel, obj) { var a = []; forEach(sel, obj, function(x) { a.push(x); }); return a; } function format(sel, arr) { sel = sel.replace(/\?/g, function() { if (arr.length === 0) throw "too few parameters given"; var p = arr.shift(); return ((typeof p === 'string') ? JSON.stringify(p) : p); }); if (arr.length) throw "too many parameters supplied"; return sel; } function compile(sel, arr) { if (arr) sel = format(sel, arr); return { sel: parse(sel)[1], match: function(obj){ return match(this.sel, obj); }, forEach: function(obj, fun) { return forEach(this.sel, obj, fun); } }; } exports._lex = lex; exports._parse = parse; exports.match = function (sel, arr, obj) { if (!obj) { obj = arr; arr = undefined; } return compile(sel, arr).match(obj); }; exports.forEach = function(sel, arr, obj, fun) { if (!fun) { fun = obj; obj = arr; arr = undefined } return compile(sel, arr).forEach(obj, fun); }; exports.compile = compile; })(typeof exports === "undefined" ? (window.JSONSelect = {}) : exports); JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/000775 000000 000000 00000000000 11710614664 021776 5ustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/conformance_tests.html000664 000000 000000 00000000644 11710614664 026404 0ustar00rootroot000000 000000 JSONSelect conformance tests These are the the official JSONQuery conformance tests.
    JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/css/000775 000000 000000 00000000000 11710614664 022566 5ustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/css/style.css000664 000000 000000 00000000766 11710614664 024451 0ustar00rootroot000000 000000 pre { color: #ddd; background-color: #333; padding: 1em; border-radius: 1em; -moz-border-radius: 1em; -webkit-border-radius: 1em; } pre.selector { text-align: center; padding: .5em; cursor: pointer; } pre.document { max-height: 150px; overflow: auto; } pre.success { background-color: #363; } pre.failure { background-color: #633; } table { width: 100%; } table td { width: 50%; } table pre { height: 100%; } h1,h2 { text-align: center; }JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/js/000775 000000 000000 00000000000 11710614664 022412 5ustar00rootroot000000 000000 JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/js/conf_tests.js000664 000000 000000 00000005634 11710614664 025127 0ustar00rootroot000000 000000 $(document).ready(function() { var tests = {}; $("#runit").click(function() { for (var k in tests) { var obj = JSON.parse($("." + k + "_document").text()); for (var i = 0; i < tests[k].length; i++) { var n = tests[k][i]; var cl = k + "_" + n; var b = $("." + cl + "_output.before"); var a = $("." + cl + "_output.after"); var s = $("." + cl + "_selector.selector"); try { a.text(""); JSONSelect.forEach(s.text(), obj, function(m) { a.text($.trim(a.text() + "\n" + JSON.stringify(m, null, " "))); }); } catch(e) { a.text("Error: " + e); } if (a.text() === b.text()) s.addClass("success").removeClass("failure"); else s.addClass("failure").removeClass("success"); } } }); function fetchFile(p, c) { $.get(p, function (data) { $("." + c).text($.trim(data)); }); } function renderTests() { function setClickToggle(cTarget, node) { cTarget.click(function() { node.toggle("medium"); }); } var c = $("
    "); for (var k in tests) { c.append($("

    ").text("document: " + k)); var cl = k + "_document"; c.append($("
    ").addClass(cl).addClass("document").text("loading document..."));
                fetchFile("tests/" + k + ".json", cl);
                for (var i = 0; i < tests[k].length; i++) {
                    var n = tests[k][i];
                    var cl = k + "_" + n + "_selector";
                    var s = $("
    ").addClass(cl).addClass("selector").text("loading selector...");
                    c.append(s);
                    fetchFile("tests/" + k + "_" + n + ".selector", cl);
                    cl = k + "_" + n + "_output";
                    var t = $("").append($("").append(
                        $("
    ").append($("
    ").addClass(cl).addClass("before").text("loading output..."))).append(
                        $("
    ").append($("
    ").addClass(cl).addClass("after").text("... test output ..."))));
    
                    c.append(t);
                    t.hide();
                    setClickToggle(s, t);
                    fetchFile("tests/" + k + "_" + n + ".output", cl + ".before");
                }
            }
            c.appendTo($("#tests"));
        }
    
        $.get("tests/alltests.txt", function (data) {
            var lines = data.split("\n");
            for (var i = 0; i < lines.length; i++) {
                var f = $.trim(lines[i]);
                if (f.length == 0) continue;
                var m = /^([A-Za-z]+)_(.+)\.selector$/.exec(f);
                if (m) {
                    if (!tests.hasOwnProperty(m[1])) tests[m[1]] = [];
                    tests[m[1]].push(m[2]);
                }
            }
            renderTests();
        });
    });
    JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/js/doctest.css000664 000000 000000 00000001616 11710614664 024575 0ustar00rootroot000000 000000 pre {
      overflow: auto;
    }
    
    pre.doctest {
      border-left: 3px solid #99f;
      padding-left: 1em;
    }
    
    pre.output {
      border-left: 3px solid #9f9;
      padding-left: 1em;
    }
    
    .doctest-example-prompt {
      color: #333;
    }
    
    .doctest-success {
      color: #060;
    }
    
    .doctest-failure {
      color: #600;
    }
    
    .doctest-example-detail {
      color: #060;
      font-weight: bold;
    }
    
    a.doctest-failure-link {
      text-decoration: none;
    }
    
    a.doctest-failure-link:hover {
      text-decoration: underline;
    }
    
    .doctest-example:target {
      border-left: 3px solid #f00;
    }
    
    div.test:target {
      border: 3px solid #ff0;
    }
    
    div.test {
      border: 1px solid #999;
      margin-bottom: 1em;
    }
    
    div.test .test-id {
      position: relative;
      float: right;
      background-color: #000;
      color: #bbb;
      padding: 3px;
    }
    
    div.test .test-id a:link,
    div.test .test-id a:visited {
      color: #bbb;
      text-decoration: none;
    }
    
    div.test .test-id a:hover {
      text-decoration: underline;
    }
    JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/js/doctest.js000664 000000 000000 00000113116 11710614664 024420 0ustar00rootroot000000 000000 /*
    
    Javascript doctest runner
    Copyright 2006-2010 Ian Bicking
    
    This program is free software; you can redistribute it and/or modify it under
    the terms of the MIT License.
    
    */
    
    
    function doctest(verbosity/*default=0*/, elements/*optional*/,
                     outputId/*optional*/) {
      var output = document.getElementById(outputId || 'doctestOutput');
      var reporter = new doctest.Reporter(output, verbosity || 0);
      if (elements) {
          if (typeof elements == 'string') {
            // Treat it as an id
            elements = [document.getElementById(elementId)];
          }
          if (! elements.length) {
              throw('No elements');
          }
          var suite = new doctest.TestSuite(elements, reporter);
      } else {
          var els = doctest.getElementsByTagAndClassName('pre', 'doctest');
          var suite = new doctest.TestSuite(els, reporter);
      }
      suite.run();
    }
    
    doctest.runDoctest = function (el, reporter) {
      logDebug('Testing element', el);
      reporter.startElement(el);
      if (el === null) {
        throw('runDoctest() with a null element');
      }
      var parsed = new doctest.Parser(el);
      var runner = new doctest.JSRunner(reporter);
      runner.runParsed(parsed);
    };
    
    doctest.TestSuite = function (els, reporter) {
      if (this === window) {
        throw('you forgot new!');
      }
      this.els = els;
      this.parsers = [];
      for (var i=0; i= this.testSuite.parsers.length) {
        logInfo('All examples from all sections tested');
        this.runner.reporter.finish();
        return;
      }
      logInfo('Testing example ' + (parserIndex+1) + ' of '
               + this.testSuite.parsers.length);
      var runNext = function () {
        self.run(parserIndex+1);
      };
      this.runner.runParsed(this.testSuite.parsers[parserIndex], 0, runNext);
    };
    
    doctest.Parser = function (el) {
      if (this === window) {
        throw('you forgot new!');
      }
      if (! el) {
        throw('Bad call to doctest.Parser');
      }
      if (el.getAttribute('parsed-id')) {
        var examplesID = el.getAttribute('parsed-id');
        if (doctest._allExamples[examplesID]) {
          this.examples = doctest._allExamples[examplesID];
          return;
        }
      }
      var newHTML = document.createElement('span');
      newHTML.className = 'doctest-example-set';
      var examplesID = doctest.genID('example-set');
      newHTML.setAttribute('id', examplesID);
      el.setAttribute('parsed-id', examplesID);
      var text = doctest.getText(el);
      var lines = text.split(/(?:\r\n|\r|\n)/);
      this.examples = [];
      var example_lines = [];
      var output_lines = [];
      for (var i=0; i/.test(line)) {
          if (! example_lines.length) {
            throw('Bad example: '+doctest.repr(line)+'\n'
                  +'> line not preceded by $');
          }
          line = line.substr(1).replace(/ *$/, '').replace(/^ /, '');
          example_lines.push(line);
        } else {
          output_lines.push(line);
        }
      }
      if (example_lines.length) {
        var ex = new doctest.Example(example_lines, output_lines);
        this.examples.push(ex);
        newHTML.appendChild(ex.createSpan());
      }
      el.innerHTML = '';
      el.appendChild(newHTML);
      doctest._allExamples[examplesID] = this.examples;
    };
    
    doctest._allExamples = {};
    
    doctest.Example = function (example, output) {
      if (this === window) {
        throw('you forgot new!');
      }
      this.example = example.join('\n');
      this.output = output.join('\n');
      this.htmlID = null;
      this.detailID = null;
    };
    
    doctest.Example.prototype.createSpan = function () {
      var id = doctest.genID('example');
      var span = document.createElement('span');
      span.className = 'doctest-example';
      span.setAttribute('id', id);
      this.htmlID = id;
      var exampleSpan = document.createElement('span');
      exampleSpan.className = 'doctest-example-code';
      var exampleLines = this.example.split(/\n/);
      for (var i=0; i 0) {
        if (this.verbosity > 1) {
          this.write('Trying:\n');
          this.write(this.formatOutput(example.example));
          this.write('Expecting:\n');
          this.write(this.formatOutput(example.output));
          this.write('ok\n');
        } else {
          this.writeln(example.example + ' ... passed!');
        }
      }
      this.success += 1;
      if ((example.output.indexOf('...') >= 0
           || example.output.indexOf('?') >= 0)
          && output) {
        example.markExample('doctest-success', 'Output:\n' + output);
      } else {
        example.markExample('doctest-success');
      }
    };
    
    doctest.Reporter.prototype.reportFailure = function (example, output) {
      this.write('Failed example:\n');
      this.write(''
                 + this.formatOutput(example.example)
                 +'');
      this.write('Expected:\n');
      this.write(this.formatOutput(example.output));
      this.write('Got:\n');
      this.write(this.formatOutput(output));
      this.failure += 1;
      example.markExample('doctest-failure', 'Actual output:\n' + output);
    };
    
    doctest.Reporter.prototype.finish = function () {
      this.writeln((this.success+this.failure)
                   + ' tests in ' + this.elements + ' items.');
      if (this.failure) {
        var color = '#f00';
      } else {
        var color = '#0f0';
      }
      this.writeln('' + this.success + ' tests of '
                   + '' + (this.success+this.failure) + ' passed, '
                   + ''
                   + this.failure + ' failed.');
    };
    
    doctest.Reporter.prototype.writeln = function (text) {
      this.write(text + '\n');
    };
    
    doctest.Reporter.prototype.write = function (text) {
      var leading = /^[ ]*/.exec(text)[0];
      text = text.substr(leading.length);
      for (var i=0; i');
      this.container.innerHTML += text;
    };
    
    doctest.Reporter.prototype.formatOutput = function (text) {
      if (! text) {
        return '    (nothing)\n';
      }
      var lines = text.split(/\n/);
      var output = '';
      for (var i=0; i= parsed.examples.length) {
        if (finishedCallback) {
          finishedCallback();
        }
        return;
      }
      var example = parsed.examples[index];
      if (typeof example == 'undefined') {
        throw('Undefined example (' + (index+1) + ' of ' + parsed.examples.length + ')');
      }
      doctest._waitCond = null;
      this.run(example);
      var finishThisRun = function () {
        self.finishRun(example);
        if (doctest._AbortCalled) {
          // FIXME: I need to find a way to make this more visible:
          logWarn('Abort() called');
          return;
        }
        self.runParsed(parsed, index+1, finishedCallback);
      };
      if (doctest._waitCond !== null) {
        if (typeof doctest._waitCond == 'number') {
          var condition = null;
          var time = doctest._waitCond;
          var maxTime = null;
        } else {
          var condition = doctest._waitCond;
          // FIXME: shouldn't be hard-coded
          var time = 100;
          var maxTime = doctest._waitTimeout || doctest.defaultTimeout;
        }
        var start = (new Date()).getTime();
        var timeoutFunc = function () {
          if (condition === null
              || condition()) {
            finishThisRun();
          } else {
            // Condition not met, try again soon...
            if ((new Date()).getTime() - start > maxTime) {
              // Time has run out
              var msg = 'Error: wait(' + repr(condition) + ') has timed out';
              writeln(msg);
              logDebug(msg);
              logDebug('Timeout after ' + ((new Date()).getTime() - start)
                       + ' milliseconds');
              finishThisRun();
              return;
            }
            setTimeout(timeoutFunc, time);
          }
        };
        setTimeout(timeoutFunc, time);
      } else {
        finishThisRun();
      }
    };
    
    doctest.formatTraceback = function (e, skipFrames) {
      skipFrames = skipFrames || 0;
      var lines = [];
      if (typeof e == 'undefined' || !e) {
        var caughtErr = null;
        try {
          (null).foo;
        } catch (caughtErr) {
          e = caughtErr;
        }
        skipFrames++;
      }
      if (e.stack) {
        var stack = e.stack.split('\n');
        for (var i=skipFrames; i ' + filename + ':' + lineno);
          }
        }
      }
      if (lines.length) {
        return lines;
      } else {
        return null;
      }
    };
    
    doctest.logTraceback = function (e, skipFrames) {
      var tracebackLines = doctest.formatTraceback(e, skipFrames);
      if (! tracebackLines) {
        return;
      }
      for (var i=0; i 1) {
          // If it's only one line it's not worth showing this
          var check = this.showCheckDifference(got, expected);
          logWarn('Mismatch of output (line-by-line comparison follows)');
          for (var i=0; i gotLines.length ?
        expectedLines.length : gotLines.length;
      function displayExpectedLine(line) {
        return line;
        line = line.replace(/\[a-zA-Z0-9_.\]\+/g, '?');
        line = line.replace(/ \+/g, ' ');
        line = line.replace(/\(\?:\.\|\[\\r\\n\]\)\*/g, '...');
        // FIXME: also unescape values? e.g., * became \*
        return line;
      }
      for (var i=0; i= expectedLines.length) {
          result.push('got extra line: ' + repr(gotLines[i]));
          continue;
        } else if (i >= gotLines.length) {
          result.push('expected extra line: ' + displayExpectedLine(expectedLines[i]));
          continue;
        }
        var gotLine = gotLines[i];
        try {
          var expectRE = new RegExp('^' + expectedLines[i] + '$');
        } catch (e) {
          result.push('regex match failed: ' + repr(gotLine) + ' ('
                + expectedLines[i] + ')');
          continue;
        }
        if (gotLine.search(expectRE) != -1) {
          result.push('match: ' + repr(gotLine));
        } else {
          result.push('no match: ' + repr(gotLine) + ' ('
                + displayExpectedLine(expectedLines[i]) + ')');
        }
      }
      return result;
    };
    
    // Should I really be setting this on RegExp?
    RegExp.escape = function (text) {
      if (!arguments.callee.sRE) {
        var specials = [
          '/', '.', '*', '+', '?', '|',
          '(', ')', '[', ']', '{', '}', '\\'
        ];
        arguments.callee.sRE = new RegExp(
          '(\\' + specials.join('|\\') + ')', 'g'
        );
      }
      return text.replace(arguments.callee.sRE, '\\$1');
    };
    
    doctest.OutputCapturer = function () {
      if (this === window) {
        throw('you forgot new!');
      }
      this.output = '';
    };
    
    doctest._output = null;
    
    doctest.OutputCapturer.prototype.capture = function () {
      doctest._output = this;
    };
    
    doctest.OutputCapturer.prototype.stopCapture = function () {
      doctest._output = null;
    };
    
    doctest.OutputCapturer.prototype.write = function (text) {
      if (typeof text == 'string') {
        this.output += text;
      } else {
        this.output += repr(text);
      }
    };
    
    // Used to create unique IDs:
    doctest._idGen = 0;
    
    doctest.genID = function (prefix) {
      prefix = prefix || 'generic-doctest';
      var id = doctest._idGen++;
      return prefix + '-' + doctest._idGen;
    };
    
    doctest.writeln = function () {
      for (var i=0; i (maxLen - indentString.length)) {
        doctest._reprTrackRestore(restorer);
        return doctest.multilineObjRepr(obj, indentString, maxLen);
      }
      return ostring;
    };
    
    doctest.multilineObjRepr = function (obj, indentString, maxLen) {
      var keys = doctest._sortedKeys(obj);
      var ostring = '{\n';
      for (var i=0; i (maxLen + indentString.length)) {
        doctest._reprTrackRestore(restorer);
        return doctest.multilineArrayRepr(obj, indentString, maxLen);
      }
      return s;
    };
    
    doctest.multilineArrayRepr = function (obj, indentString, maxLen) {
      var s = "[\n";
      for (var i=0; i';
      return s;
    };
    
    doctest.repr.registry = [
        [function (o) {
             return typeof o == 'string';},
         function (o) {
             o = '"' + o.replace(/([\"\\])/g, '\\$1') + '"';
             o = o.replace(/[\f]/g, "\\f")
             .replace(/[\b]/g, "\\b")
             .replace(/[\n]/g, "\\n")
             .replace(/[\t]/g, "\\t")
             .replace(/[\r]/g, "\\r");
             return o;
         }],
        [function (o) {
             return typeof o == 'number';},
         function (o) {
             return o + "";
         }],
        [function (o) {
              return (typeof o == 'object' && o.xmlVersion);
         },
         doctest.xmlRepr],
        [function (o) {
             var typ = typeof o;
             if ((typ != 'object' && ! (type == 'function' && typeof o.item == 'function')) ||
                 o === null ||
                 typeof o.length != 'number' ||
                 o.nodeType === 3) {
                 return false;
             }
             return true;
         },
         doctest.arrayRepr
         ]];
    
    doctest.objDiff = function (orig, current) {
      var result = {
        added: {},
        removed: {},
        changed: {},
        same: {}
      };
      for (var i in orig) {
        if (! (i in current)) {
          result.removed[i] = orig[i];
        } else if (orig[i] !== current[i]) {
          result.changed[i] = [orig[i], current[i]];
        } else {
          result.same[i] = orig[i];
        }
      }
      for (i in current) {
        if (! (i in orig)) {
          result.added[i] = current[i];
        }
      }
      return result;
    };
    
    doctest.writeDiff = function (orig, current, indentString) {
      if (typeof orig != 'object' || typeof current != 'object') {
        writeln(indentString + repr(orig, indentString) + ' -> ' + repr(current, indentString));
        return;
      }
      indentString = indentString || '';
      var diff = doctest.objDiff(orig, current);
      var i, keys;
      var any = false;
      keys = doctest._sortedKeys(diff.added);
      for (i=0; i '
                + repr(diff.changed[keys[i]][1], indentString));
      }
      if (! any) {
        writeln(indentString + '(no changes)');
      }
    };
    
    doctest.objectsEqual = function (ob1, ob2) {
      var i;
      if (typeof ob1 != 'object' || typeof ob2 != 'object') {
        return ob1 === ob2;
      }
      for (i in ob1) {
        if (ob1[i] !== ob2[i]) {
          return false;
        }
      }
      for (i in ob2) {
        if (! (i in ob1)) {
          return false;
        }
      }
      return true;
    };
    
    doctest.getElementsByTagAndClassName = function (tagName, className, parent/*optional*/) {
        parent = parent || document;
        var els = parent.getElementsByTagName(tagName);
        var result = [];
        var regexes = [];
        if (typeof className == 'string') {
          className = [className];
        }
        for (var i=0; i/g, '>');
    };
    
    doctest.escapeSpaces = function (s) {
      return s.replace(/  /g, '  ');
    };
    
    doctest.extend = function (obj, extendWith) {
        for (i in extendWith) {
            obj[i] = extendWith[i];
        }
        return obj;
    };
    
    doctest.extendDefault = function (obj, extendWith) {
        for (i in extendWith) {
            if (typeof obj[i] == 'undefined') {
                obj[i] = extendWith[i];
            }
        }
        return obj;
    };
    
    if (typeof repr == 'undefined') {
        repr = doctest.repr;
    }
    
    doctest._consoleFunc = function (attr) {
      if (typeof window.console != 'undefined'
          && typeof window.console[attr] != 'undefined') {
        if (typeof console[attr].apply === 'function') {
          result = function() {
            console[attr].apply(console, arguments);
          };
        } else {
          result = console[attr];
        }
      } else {
        result = function () {
          // FIXME: do something
        };
      }
      return result;
    };
    
    if (typeof log == 'undefined') {
      log = doctest._consoleFunc('log');
    }
    
    if (typeof logDebug == 'undefined') {
      logDebug = doctest._consoleFunc('log');
    }
    
    if (typeof logInfo == 'undefined') {
      logInfo = doctest._consoleFunc('info');
    }
    
    if (typeof logWarn == 'undefined') {
      logWarn = doctest._consoleFunc('warn');
    }
    
    doctest.eval = function () {
      return window.eval.apply(window, arguments);
    };
    
    doctest.useCoffeeScript = function (options) {
      options = options || {};
      options.bare = true;
      options.globals = true;
      if (! options.fileName) {
        options.fileName = 'repl';
      }
      if (typeof CoffeeScript == 'undefined') {
        doctest.logWarn('coffee-script.js is not included');
        throw 'coffee-script.js is not included';
      }
      doctest.eval = function (code) {
        var src = CoffeeScript.compile(code, options);
        logDebug('Compiled code to:', src);
        return window.eval(src);
      };
    };
    
    doctest.autoSetup = function (parent) {
      var tags = doctest.getElementsByTagAndClassName('div', 'test', parent);
      // First we'll make sure everything has an ID
      var tagsById = {};
      for (var i=0; i 1) {
        // This makes the :target CSS work, since if the hash points to an
        // element whose id has just been added, it won't be noticed
        location.hash = location.hash;
      }
      var output = document.getElementById('doctestOutput');
      if (! tags.length) {
        tags = document.getElementsByTagName('body');
      }
      if (! output) {
        output = document.createElement('pre');
        output.setAttribute('id', 'doctestOutput');
        output.className = 'output';
        tags[0].parentNode.insertBefore(output, tags[0]);
      }
      var reloader = document.getElementById('doctestReload');
      if (! reloader) {
        reloader = document.createElement('button');
        reloader.setAttribute('type', 'button');
        reloader.setAttribute('id', 'doctest-testall');
        reloader.innerHTML = 'test all';
        reloader.onclick = function () {
          location.hash = '#doctest-testall';
          location.reload();
        };
        output.parentNode.insertBefore(reloader, output);
      }
    };
    
    doctest.autoSetup._idCount = 0;
    
    doctest.Spy = function (name, options, extraOptions) {
      var self;
      if (doctest.spies[name]) {
         self = doctest.spies[name];
         if (! options && ! extraOptions) {
           return self;
         }
      } else {
        self = function () {
          return self.func.apply(this, arguments);
        };
      }
      name = name || 'spy';
      options = options || {};
      if (typeof options == 'function') {
        options = {applies: options};
      }
      if (extraOptions) {
        doctest.extendDefault(options, extraOptions);
      }
      doctest.extendDefault(options, doctest.defaultSpyOptions);
      self._name = name;
      self.options = options;
      self.called = false;
      self.calledWait = false;
      self.args = null;
      self.self = null;
      self.argList = [];
      self.selfList = [];
      self.writes = options.writes || false;
      self.returns = options.returns || null;
      self.applies = options.applies || null;
      self.binds = options.binds || null;
      self.throwError = options.throwError || null;
      self.ignoreThis = options.ignoreThis || false;
      self.wrapArgs = options.wrapArgs || false;
      self.func = function () {
        self.called = true;
        self.calledWait = true;
        self.args = doctest._argsToArray(arguments);
        self.self = this;
        self.argList.push(self.args);
        self.selfList.push(this);
        // It might be possible to get the caller?
        if (self.writes) {
          writeln(self.formatCall());
        }
        if (self.throwError) {
          throw self.throwError;
        }
        if (self.applies) {
          return self.applies.apply(this, arguments);
        }
        return self.returns;
      };
      self.func.toString = function () {
        return "Spy('" + self._name + "').func";
      };
    
      // Method definitions:
      self.formatCall = function () {
        var s = '';
        if ((! self.ignoreThis) && self.self !== window && self.self !== self) {
          s += doctest.repr(self.self) + '.';
        }
        s += self._name;
        if (self.args === null) {
          return s + ':never called';
        }
        s += '(';
        for (var i=0; i');
      }
      var loc = window.location.search.substring(1);
      if (auto || (/doctestRun/).exec(loc)) {
        var elements = null;
        // FIXME: we need to put the output near the specific test being tested:
        if (location.hash) {
          var el = document.getElementById(location.hash.substr(1));
          if (el) {
            if (/\btest\b/.exec(el.className)) {
              var testEls = doctest.getElementsByTagAndClassName('pre', 'doctest', el);
              elements = doctest.getElementsByTagAndClassName('pre', ['doctest', 'setup']);
              for (var i=0; i
      
        JSONSelect JS lex tests
        
        
        
        
      
      
    
    
    
    

    Tests of the JSONSelect lexer

    Simple tokens
    $ JSONSelect._lex(">");
    [1, ">"]
    $ JSONSelect._lex("*");
    [1, "*"]
    $ JSONSelect._lex(",");
    [1, ","]
    $ JSONSelect._lex(".");
    [1, "."]
    
    Offsets
    $ JSONSelect._lex("foobar>",6);
    [7, ">"]
    
    Types
    $ JSONSelect._lex("string");
    [6, 3, "string"]
    $ JSONSelect._lex("boolean");
    [7, 3, "boolean"]
    $ JSONSelect._lex("null");
    [4, 3, "null"]
    $ JSONSelect._lex("array");
    [5, 3, "array"]
    $ JSONSelect._lex("object");
    [6, 3, "object"]
    $ JSONSelect._lex("number");
    [6, 3, "number"]
    
    Whitespace
    $ JSONSelect._lex("\r");
    [1, " "]
    $ JSONSelect._lex("\n");
    [1, " "]
    $ JSONSelect._lex("\t");
    [1, " "]
    $ JSONSelect._lex(" ");
    [1, " "]
    $ JSONSelect._lex("     \t   \r\n  !");
    [13, " "]
    
    pseudo classes
    $ JSONSelect._lex(":root");
    [5, 1, ":root"]
    $ JSONSelect._lex(":first-child");
    [12, 1, ":first-child"]
    $ JSONSelect._lex(":last-child");
    [11, 1, ":last-child"]
    $ JSONSelect._lex(":only-child");
    [11, 1, ":only-child"]
    
    json strings
    $ JSONSelect._lex('"foo bar baz"');
    [13, 4, "foo bar baz"]
    $ JSONSelect._lex('"\\u0020"');
    [8, 4, " "]
    $ JSONSelect._lex('\"not terminated');
    Error: unclosed json string
    $ JSONSelect._lex('"invalid escape: \\y"');
    Error: invalid json string
    
    identifiers (like after '.')
    $ JSONSelect._lex("foo");
    [3, 4, "foo"]
    $ JSONSelect._lex("foo\\ bar");
    [8, 4, "foo bar"]
    $ JSONSelect._lex("_aB129bcde-\\:foo\\@$");
    [18, 4, "_aB129bcde-:foo@"]
    
    non-ascii
    $ JSONSelect._lex("обичам\\ те\\!");
    [12, 4, "обичам те!"]
    
    JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/match_test.html000664 000000 000000 00000003450 11710614664 025021 0ustar00rootroot000000 000000 JSONSelect JS matching tests
    
    

    Tests of the JSONSelect matcher

    Types
    $ JSONSelect.match("null", null);
    [null]
    $ JSONSelect.match("array", { 1: [], 2: [] });
    [[], []]
    $ JSONSelect.match("object", [ {}, {} ]);
    [{}, {}]
    $ JSONSelect.match("string", [ "a", 1, true, null, false, "b", 3.1415, "c" ] );
    ["a", "b", "c"]
    $ JSONSelect.match("boolean", [ "a", 1, true, null, false, "b", 3.1415, "c" ] );
    [true, false]
    $ JSONSelect.match("number", [ "a", 1, true, null, false, "b", 3.1415, "c" ] );
    [1, 3.1415]
    
    IDs
    $ JSONSelect.match(".foo", {foo: "aMatch", bar: [ { foo: "anotherMatch" } ] });
    ["aMatch", "anotherMatch"]
    
    Descendants
    $ JSONSelect.match(".foo .bar", {foo: { baz: 1, bar: 2 }, bar: 3});
    [2]
    $ JSONSelect.match(".foo > .bar", {foo: { baz: 1, bar: 2 }, bar: 3});
    [2]
    $ JSONSelect.match(".foo > .bar", {foo: { baz: { bar: 4 }, bar: 2 }, bar: 3});
    [2]
    $ JSONSelect.match(".foo .bar", {foo: { baz: { bar: 4 }, bar: 2 }, bar: 3});
    [4, 2]
    
    Grouping
    $ JSONSelect.match("number,boolean", [ "a", 1, true, null, false, "b", 3.1415, "c" ] );
    [1, true, false, 3.1415]
    $ JSONSelect.match("number,boolean,null", [ "a", 1, true, null, false, "b", 3.1415, "c" ] );
    [1, true, null, false, 3.1415]
    
    JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/parse_test.html000664 000000 000000 00000005540 11710614664 025041 0ustar00rootroot000000 000000 JSONSelect JS parser tests
    
    

    Tests of the JSONSelect parser

    Selectors
    $ JSONSelect._parse(".foo");
    [{id: "foo"}]
    $ JSONSelect._parse('." foo "');
    [{id: " foo "}]
    $ JSONSelect._parse("string.foo:last-child");
    [{a: 0, b: 1, id: "foo", pf: ":nth-last-child", type: "string"}]
    $ JSONSelect._parse("string.foo.bar");
    Error: multiple ids not allowed
    $ JSONSelect._parse("string.foo:first-child.bar");
    Error: multiple ids not allowed
    $ JSONSelect._parse("string:last-child.foo:first-child.bar");
    Error: multiple pseudo classes (:xxx) not allowed
    $ JSONSelect._parse("string.");
    Error: string required after '.'
    $ JSONSelect._parse("string:bogus");
    Error: unrecognized pseudo class
    $ JSONSelect._parse("string.xxx\\@yyy");
    [{id: "xxx@yyy", type: "string"}]
    $ JSONSelect._parse(" ");
    Error: selector expected
    $ JSONSelect._parse("");
    Error: selector expected
    
    Combinators
    $ JSONSelect._parse(".foo .bar");
    [{id: "foo"}, {id: "bar"}]
    $ JSONSelect._parse("string.foo , number.foo");
    [",", [{id: "foo", type: "string"}], [{id: "foo", type: "number"}]]
    $ JSONSelect._parse("string > .foo number.bar");
    [{type: "string"}, ">", {id: "foo"}, {id: "bar", type: "number"}]
    $ JSONSelect._parse("string > .foo number.bar, object");
    [",", [{type: "string"}, ">", {id: "foo"}, {id: "bar", type: "number"}], [{type: "object"}]]
    $ JSONSelect._parse("string > .foo number.bar, object, string, .\"baz bing\", :root");
    [
      ",",
      [{type: "string"}, ">", {id: "foo"}, {id: "bar", type: "number"}],
      [{type: "object"}],
      [{type: "string"}],
      [{id: "baz bing"}],
      [{pc: ":root"}]
    ]
    
    Expressions
    $ JSONSelect._parse(":nth-child(1)");
    [{a: 0, b: 1, pf: ":nth-child"}]
    $ JSONSelect._parse(":nth-child(2n+1)");
    [{a: 2, b: 1, pf: ":nth-child"}]
    $ JSONSelect._parse(":nth-child ( 2n + 1 )");
    [{a: 2, b: 1, pf: ":nth-child"}]
    $ JSONSelect._parse(":nth-child(odd)");
    [{a: 2, b: 1, pf: ":nth-child"}]
    $ JSONSelect._parse(":nth-child(even)");
    [{a: 2, b: 0, pf: ":nth-child"}]
    $ JSONSelect._parse(":nth-child(-n+6)");
    [{a: -1, b: 6, pf: ":nth-child"}]
    $ JSONSelect._parse(":nth-child(2n)");
    [{a: 2, b: 0, pf: ":nth-child"}]
    $ JSONSelect._parse(":nth-last-child(-3n - 3)");
    [{a: -3, b: -3, pf: ":nth-last-child"}]
    $ JSONSelect._parse(":first-child");
    [{a: 0, b: 1, pf: ":nth-child"}]
    $ JSONSelect._parse(":last-child");
    [{a: 0, b: 1, pf: ":nth-last-child"}]
    
    JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/run.js000664 000000 000000 00000005205 11710614664 023142 0ustar00rootroot000000 000000 /* * a node.js test runner that executes all conformance * tests and outputs results to console. * Process returns zero on success, non-zero on failure. */ const fs = require('fs'), path = require('path'), jsonselect = require('../jsonselect.js'), sys = require('sys'); var pathToTests = path.join(__dirname, "tests"); // a map: document nametest name -> list of se var numTests = 0; var numPassed = 0; var tests = {}; function runOneSync(name, selname, p) { var testDocPath = path.join(p, name + ".json"); var selDocPath = path.join(p, name + '_' + selname + ".selector"); var outputDocPath = selDocPath.replace(/selector$/, "output"); // take `obj`, apply `sel, get `got`, is it what we `want`? var obj = JSON.parse(fs.readFileSync(testDocPath)); var want = String(fs.readFileSync(outputDocPath)).trim(); var got = ""; var sel = String(fs.readFileSync(selDocPath)).trim(); try { jsonselect.forEach(sel, obj, function(m) { got += JSON.stringify(m, undefined, 4) + "\n"; }); } catch(e) { got = e.toString(); if (want.trim() != got.trim()) throw e; } if (want.trim() != got.trim()) throw "mismatch"; } function runTests() { console.log("Running Tests:"); for (var l in tests) { for (var d in tests[l]) { console.log(" level " + l + " tests against \"" + d + ".json\":"); for (var i = 0; i < tests[l][d].length; i++) { sys.print(" " + tests[l][d][i][0] + ": "); try { runOneSync(d, tests[l][d][i][0], tests[l][d][i][1]); numPassed++; console.log("pass"); } catch (e) { console.log("fail (" + e.toString() + ")"); } } } } console.log(numPassed + "/" + numTests + " passed"); process.exit(numPassed == numTests ? 0 : 1); } // discover all tests var pathToTests = path.join(__dirname, "tests"); fs.readdirSync(pathToTests).forEach(function(subdir) { var p = path.join(pathToTests, subdir); if (!fs.statSync(p).isDirectory()) return; var l = /^level_([\d+])$/.exec(subdir); if (!l) return; l = l[1]; var files = fs.readdirSync(p); for (var i = 0; i < files.length; i++) { var f = files[i]; var m = /^([A-Za-z]+)_(.+)\.selector$/.exec(f); if (m) { if (!tests.hasOwnProperty(l)) tests[l] = []; if (!tests[l].hasOwnProperty(m[1])) tests[l][m[1]] = []; numTests++; tests[l][m[1]].push([m[2], p]); } } }); runTests(); JSONSelect-d642d73c1ff71627fc989e194875725c71b8d5a2/src/test/tests/000775 000000 000000 00000000000 11710614664 023140 5ustar00rootroot000000 000000