diff --git a/.travis.yml b/.travis.yml index 954609f8..7396b16b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,15 @@ node_js: - 0.6 - 0.8 - 0.10 + - 0.11 + +matrix: + allow_failures: + - node_js: '0.11' + notifications: #webhooks: #http://requestb.in/12h5bl71 email: on_success: never - on_failure: change \ No newline at end of file + on_failure: change diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83671978..f965b779 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ so to get it you should type a couple more commands: Commit --------------- -Format of the commit message: **type(scope): subject** +Format of the commit message: **type(scope) subject** **Type**: - feature @@ -45,5 +45,3 @@ For example util, console, view, edit, style etc... **Examples**: - [fix(style) .name{width}: 37% -> 35%](https://github.com/coderaiser/cloudcmd/commit/94b0642e3990c17b3a0ee3efeb75f343e1e7c050) - [fix(console) dispatch: focus -> mouseup](https://github.com/coderaiser/cloudcmd/commit/f41ec5058d1411e86a881f8e8077e0572e0409ec) - -**Big change should be writed in ChangeLog like [this](https://github.com/coderaiser/cloudcmd/commit/e1893f77be09585decf8a260d45a42efc11c98e5).** \ No newline at end of file diff --git a/ChangeLog b/ChangeLog index 1f8aebfe..950d2320 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,109 @@ +2012.09.27, v0.4.0 + +feature: +- (edit) add showMessage +- (config) add localStorage +- (config) add analytics +- (socket) max reconnection attempts: 1000000 -> Math.pow- (2, 64) +- (css) .cmd-button - (601px - 785px): 15% -> 13% +- (css) hide f5, f6 on one-panel-view +- (update) git pull -> git pull --rebase +- (client) add ability to upload files on drag'n'drop +- (client) add route +- (socket) add reconnection delay, max attempts +- (console) rm jquery-migrate +- (edit) rm "nothing to save" +- (menu, dom) add zipFile + +update: +- (view) fancyBox v2.1.4 -> v2.1.5 + +fix: +- (client) ajaxLoad fs: removeStr -> removeStrOneTime +- (cloudcmd) indexProcessing: appcache +- (rest) onFS: lName -> p.name, p.name -> lName +- (cloudfunc) ";" -> "," +- (client) route -> CloudCmd.route +- (dom) getCurrentFileContent: add dataType check +- (dom) Loader ajax: add dataType +- (rest) stream.createPipe -> pipe.create +- (socket) Win32 -> WIN32 +- (socket) crash on win7 +- (main) add stream +- (view) show: add createTextNode +- (css) .icon height: 14.95 -> 14.8- (ie) +- (css) .icon height: 15 -> 14.95- (ie) +- (css) .icon height, width: 15.5 -> 15 +- (client) unload, beforeunload +- (dom) jqueryLoad: pCallBack -> onload: pCallBack +- (dom) jqueryLoad: add pCallBack +- (dom) retJSLoad: this -> Loader +- (test) keyBinding -> key, ie -> polyfill, viewer -> view, editor/_codemirror -> edit, terminal -> console +- (edit) change ace to noconflict +- (filepicker) add mimetype "", rm default ".txt" ext +- (main) sendError +- (socket) reconnect +- (server) Dir + Name -> path.join- (Dir, Name) +- (rest) add lReadStream.on- (error, lError) +- (css) .icon: {width, height: 16px -> 15.5px} + +inside: +- (dom) DOMTreeProto: add DOM +- (util) add asyncCall +- (listeners) appCache: add getConfig +- (socket) add transports +- (socket) add browser client etag +- (socket) lListent: set -> enable +- (css) rm .error:hover +- (view,edit,console,menu) add Loading check +- (index) rm "if lt IE" +- (jshint) rm es5: ES5 option is now set per default +- (dom) Loader: add xhr.upload progress +- (jshint) rm forint - (should be wrapped in an if) +- (dom) Images: add return img +- (dom) Loader: add responseType +- (jshint) add expr: "if ExpressionStatement should be allowed as Programs" +- (clieant) multiple files load, readAsText -> readAsArrayBuffer +- (util) add isArrayBuffer +- (util) exec: any count of args +- (shell) add log +- (css) .cmd-button: add transition +- (css) selection -> user-select +- (cloudcmd) rm no-js redirect +- (view) add minWidth, minHeight +- (dom) {setSelected,unsetSelected}File -> toggleSelectedFile +- (client) add panel backlight on drag +- (dom) add toggleClass +- (dom) add DOM.getCurrentFileByName +- (util) add getStrBigFirst +- (stream) putFile -> stream.createPipe +- (css) .current-file: add transition +- (rest) add zip +- (cloudfunc) add format +- (dom) Loader ajax: add TYPE_JSON +- (client) mv Events.add to listeners +- (client) google-analytics -> analytics +- (stream) stream -> pipe +- (css) display, width, height: .icon -> .loading +- (css) font-family: .icon -> .error::before +- (dom) Images showError: rm textStatus, errorThrown +- (dom) Loader: mv percent, count, msg to if +- (update) lStdout -> lMsg +- (css) mv .panel, #right to max-width:1155px +- (dom) ajax, global XMLHTTP -> local xhr +- (dom) addClass, removeClass, toggleClass +- (dom) removeClass: Events -> DOMTree +- (edit) add initAce, worker condition +- (dom) Events: process +- (client) loadModule: getStrBigFirst +- (rest) put: /fs/file?zip -> /zip +- (rest) put cp: add putFile +- (rest) putFile callback: pError, pMsg -> pError +- (rest) main.sendResponse -> sendMsg +- (rest) add putFile +- (css) rm !important +- (css) path-icon: rm font-family, font-size + 2012.07.01, v0.3.0 * Changed jquery cdn to one with https suport jquery.com -> google cdn. diff --git a/CodingStyle b/CodingStyle deleted file mode 100644 index cc8e4a75..00000000 --- a/CodingStyle +++ /dev/null @@ -1,15 +0,0 @@ -Правила названий: - varName - имя функции - lVarName - имя локальной переменной - pVarName - имя параметра - VarName_f - имя функции созданной внутри функции - VARNAME - имя константы - -Varible types: - varName_s - string - varName_n - number - varName_o - object - varName_a - array - -SS - Server Side; -CS - Client Side; \ No newline at end of file diff --git a/README.md b/README.md index aa364275..0c869487 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,22 @@ -Cloud Commander v0.3.0 [![NPM version][NPMIMGURL]][NPMURL] [![Dependency Status][DependencyStatusIMGURL]][DependencyStatusURL] [![Build Status][BuildStatusIMGURL]][BuildStatusURL] +Cloud Commander v0.4.0 [![NPM version][NPMIMGURL]][NPMURL] [![Dependency Status][DependencyStatusIMGURL]][DependencyStatusURL] [![Build Status][BuildStatusIMGURL]][BuildStatusURL] =============== +###[Main][MainURL] [Blog][BlogURL] [Demo][DemoURL] [![Flattr][FlattrIMGURL]][FlattrURL] [NPMIMGURL]: https://badge.fury.io/js/cloudcmd.png [BuildStatusIMGURL]: https://secure.travis-ci.org/coderaiser/cloudcmd.png?branch=master [DependencyStatusIMGURL]: https://gemnasium.com/coderaiser/cloudcmd.png [FlattrIMGURL]: http://api.flattr.com/button/flattr-badge-large.png +[NPM_INFO_IMG]: https://nodei.co/npm/cloudcmd.png?downloads=true&&stars [NPMURL]: http://badge.fury.io/js/cloudcmd [BuildStatusURL]: http://travis-ci.org/coderaiser/cloudcmd "Build Status" [DependencyStatusURL]: https://gemnasium.com/coderaiser/cloudcmd "Dependency Status" -[FlattrURL]: https://flattr.com/submit/auto?user_id=coderaiser&url=github.com/coderaiser/cloudcmd&title=cloudcmd&language=&tags=github&category=software +[FlattrURL]: https://flattr.com/submit/auto?user_id=coderaiser&url=github.com/coderaiser/cloudcmd&title=cloudcmd&language=&tags=github&category=software "flattr" +[NPM_INFO_URL]: https://npmjs.org/package/cloudcmd "npm" +[MainURL]: http://cloudcmd.io "Main" +[BlogURL]: http://blog.cloudcmd.io "Blog" +[DemoURL]: http://io.cloudcmd.io "Demo" -**Cloud Commander** - user friendly cloud file manager. -DEMO: -[cloudfoundry] (https://cloudcmd.cloudfoundry.com "cloudfoundry"), -[appfog] (https://cloudcmd.aws.af.cm "appfog"), -[jitsu] (https://cloudcmd.jit.su "jitsu"). - -Google PageSpeed Score : [100](//developers.google.com/speed/pagespeed/insights#url=http_3A_2F_2Fcloudcmd.aws.af.cm_2F&mobile=false "score") (out of 100) -(or 96 if js or css minification disabled in config.json). +**Cloud Commander** - cloud file manager with console and editor. ![Cloud Commander](/img/logo/cloudcmd.png "Cloud Commander") @@ -27,15 +26,15 @@ Benefits - responsible design - one full page loading, *and then just one-time json-dir-listings loading (with refresh opportunity).* -- caching readed directories *to localStorage (for now) -(so if network will disconnected or something heppen with a signal, we +- caching read directories *to localStorage (for now) +(so if network will disconnected or something happens with a signal, we definitely will can work with cached copy of directory listings)*; - key binding - disabled js support *(working in limited mode)*. - automated minification *client js-files and onstart-reading Cloud manager files on server starting.* **Cloud Commander** uses all benefits of js, so if js is disabled, -we moves to *limited mode*. +we go to *limited mode*. Limited-mode features --------------- @@ -55,14 +54,14 @@ There is a short list: - **F6** - rename/move - **F7** - new dir - **F8, Delete** - remove current file -- **F9** = menu +- **F9** - menu - **Ctrl + r** - reload dir content - **Ctrl + d** - clear local cache (wich contains dir contents) - **Alt + q** - disable key bindings - **Alt + s** - get all key bindings back -- **Ctrl + A** - select all files in a panel +- **Ctrl + a** - select all files in a panel - **up, down, enter** - filesystem navigation -- **Tab** - move thru panels +- **Tab** - move via panels - **Page Up** - up on one page - **Page Down** - down on one page - **Home** - to begin of list @@ -72,20 +71,46 @@ There is a short list: - **Shift + F10** - context menu - **~** - console -Viewer's hot keys +Edit --------------- -- **F3** - open -- **Esc** - close +[Demo](http://io.cloudcmd.io/fs/etc#/edit/passwd "Edit") +![Edit](/img/screenshot/edit.png "Edit") -Editor's hot keys ---------------- +###Hot keys - **F4** - open - **Ctrl + s** - save - **Esc** - close +Console +--------------- +[Demo](http://io.cloudcmd.io#/console "Console") +![Console](/img/screenshot/console.png "Console") + +###Hot keys +- **~** - open +- **Esc** - close + Menu --------------- -Right mouse click button show context menu with items: +[Demo](http://io.cloudcmd.io#/menu "Menu") +![Menu](/img/screenshot/menu.png "Menu") +Right mouse click button shows context menu with items: +- View +- Edit +- Rename +- Delete +- Zip file +- Upload to (Dropbox, Github, GDrive) +- Download +- New (File, Dir, from cloud) + +###Hot keys +- **F9** - open +- **Esc** - close + +Menu +--------------- +Right mouse click button shows context menu with items: - View - Edit - Rename @@ -96,9 +121,11 @@ Right mouse click button show context menu with items: Install --------------- -**Cloud Commander** install is very easy. +[![NPM_INFO][NPM_INFO_IMG]][NPM_INFO_URL] + +Installing **Cloud Commander** is very simple. All you need is -- install [node.js](//nodejs.org/ "node.js") +- install [node.js](http://nodejs.org/ "node.js") - [download](https://github.com/coderaiser/cloudcmd/archive/master.zip) and unpack or just clone repository from github: @@ -115,27 +142,29 @@ or install in npm: Configuration --------------- -All main configuration could be done thrue config.json. +All main configuration could be done via config.json. ```js { - "api_url" :"/api/v1", - "appcache" : false, /* html5 feature appcache */ - "cache" : true, /* cashing on a client */ + "api_url" :"/api/v1", + "appcache" : false, /* cache files for offline use */ + "analytics" : true, /* google analytics suport */ + "localStorage" : true, /* cache directory data */ "minification" : { /* minification of js,css,html and img */ - "js" : false, /* minify module neaded */ + "js" : false, /* minify module needed */ "css" : false, /* npm i minify */ "html" : true, "img" : false }, - "show_keys_panel" : true, /* show classic panel with buttons of keys */ - "server" : true, /* server mode or testing mode */ - "logs" : false, /* logs or console ouput */ - "socket" : true /* enable web sockets */ - "port" : 80, /* http port or null(default) */ - "sslPort" : 443, /* https port or null(default) */ - "ip" : "127.0.0.1", /* ip or null(default) */ - "ssl" : true /* should use https? */ - "rest" : true /* enable rest interface */ + "cache" : true, + "logs" : false, /* logs or console ouput */ + "show_keys_panel" : true, /* show classic panel with buttons of keys */ + "server" : true, /* server mode or testing mode */ + "socket" : true /* enable web sockets */ + "port" : 8000, /* http port or null(default) */ + "sslPort" : 443, /* https port or null(default) */ + "ip" : null, /* ip or null(default) */ + "ssl" : false /* should use https? */ + "rest" : true /* enable rest interface */ } ``` @@ -144,27 +173,51 @@ Server Standard practices say no non-root process gets to talk to the Internet on a port less than 1024. Anyway I suggest you to start Cloud Commander as non-root. How it could be solved? -There is a couple easy and fast ways. One of them is port forwarding by iptables. +There is a couple easy and fast ways. One of them is port forwarding. +###Iptables Just run [shell/addtables.sh](shell/addtables.sh) for default options. - ```sh @:/tmp/cloudcmd (dev) $ sudo iptables -t nat -L # look rules before @:/tmp/cloudcmd (dev) $ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8000 @:/tmp/cloudcmd (dev) $ sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 4430 -@:/tmp/cloudcmd (dev) $ sudo iptables -t nat -L # look reles after +@:/tmp/cloudcmd (dev) $ sudo iptables -t nat -L # look rules after ``` -You should see somethins like this ( **8000** and **4430** should be in config as **port** and **sslPort** ) +You should see something like this ( **8000** and **4430** should be in config as **port** and **sslPort** ) target prot opt source destination REDIRECT tcp -- anywhere anywhere tcp dpt:http redir ports 8000 REDIRECT tcp -- anywhere anywhere tcp dpt:https redir ports 4430 -If you would want to get things back just clear rules ( **1** and **2** it's rules numbers, +If you would want to get things back just clear rules ( **1** and **2** it's rule numbers, in your list they could differ). ```sh -@:/tmp/cloudcmd (dev) $ sudo iptables -t nat -D PREROUTING 1 @:/tmp/cloudcmd (dev) $ sudo iptables -t nat -D PREROUTING 2 +@:/tmp/cloudcmd (dev) $ sudo iptables -t nat -D PREROUTING 1 +``` + +###nginx +Get [nginx](http://nginx.org/ "nginx"). On linux it could be done like that +```sh +sudo apt-get install nginx #for ubuntu and debian +``` +Than make host file **/etc/nginx/sites-enabled/io.cloudcmd.io** +( *io.cloudcmd.io* is your domain name) with content: +```sh +server { + listen 80; + server_name io.cloudcmd.io; + access_log /var/log/nginx/io.cloudcmd.io.access.log; + location / { + proxy_pass http://127.0.0.1:8000/; + } +} +``` +```sh +# create symlink of this file +ln -s ./sites-enabled/io.cloudcmd.io ./sites-available +# restart nginx +/etc/init.d/nginx restart ``` To run Cloud Commander as daemon in linux you could set **log** to true in config and @@ -174,31 +227,29 @@ do something like this: Authorization --------------- -Thru openID Cloud Commander could authorize clients on GitHub. +Cloud Commander could authorize clients on GitHub via openID. All things that should be done is must be added **id** and **secret** of application -from github settings page and added to **config.json** (id just) and env varible (secret) -with names: *github_id*, *github_secret*, *dropbox_key*, *dropbox_secret* etc. -For more information see **config.json** and **shell/seret.bat** *(on win32)* -or **shell/secret.sh** *(on nix)*. +from github settings page and added to [modules.json](json/modules.json) (id just) and env variable (secret) +with names: *github_id*, *github_secret*, *dropbox_key*, *dropbox_secret* etc in +[secret.bat](shell/secret.bat) *(on win32)* or [secret.sh](shell/secret.sh) *(on nix)*. Start --------------- -To start **Cloud Commander** only one command neaded: +To start **Cloud Commander** only one command needed: node cloudcmd or on win platform just cloudcmd -After thet Cloud Commander reads config file **config.json** and start server -on 80 port, if none of port varibles(*cloud9*, *cloudfoundry* and *nodester*) -isn't exist. +After that Cloud Commander reads port information from config file [config.json](json/config.json#L17) and start server +on this port ( **8000** by default ), if none of port varibles ( *cloud9*, *cloudfoundry* and *nodester* ) isn't exist. Then type in browser - http://127.0.0.1 + http://127.0.0.1:8000 or - http://localhost + http://localhost:8000 Update --------------- **Cloud Commander** is very often updates. @@ -254,7 +305,7 @@ Getting dev version of **Cloud Commander**: git clone git://github.com/coderaiser/cloudcmd.git git checkout dev -It is possible thet dev version Cloud Commander will needed dev version of Minify, +It is possible that dev version of Cloud Commander will needed dev version of Minify, so to get it you should type a couple more commands: cd node_modules @@ -264,9 +315,10 @@ so to get it you should type a couple more commands: Version history --------------- -- *2012.07.01*, **[v0.3.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.3.0.zip)** -- *2012.04.22*, **[v0.2.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.2.0.zip)** -- *2012.03.01*, **[v0.1.9](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.9.zip)** +- *2013.09.27*, **[v0.4.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.4.0.zip)** +- *2013.07.01*, **[v0.3.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.3.0.zip)** +- *2013.04.22*, **[v0.2.0](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.2.0.zip)** +- *2013.03.01*, **[v0.1.9](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.9.zip)** - *2012.12.12*, **[v0.1.8](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.8.zip)** - *2012.10.01*, **[v0.1.7](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.7.zip)** - *2012.08.24*, **[v0.1.6](//github.com/coderaiser/cloudcmd-archive/raw/master/cloudcmd-v0.1.6.zip)** @@ -283,5 +335,5 @@ MIT [license](LICENSE "license"). Special Thanks --------------- -[Elena Zalitok](http://vk.com/politilena "Elena Zalitok") for -[logo](img/logo/cloudcmd.png "logo") and [favicon](img/favicon/favicon.png "favicon"). +- [Polietilena](http://polietilena.github.io/ "Polietilena") for [logo](img/logo/cloudcmd.png "logo") and [favicon](img/favicon/favicon.png "favicon"). +- [Elec-ua](https://github.com/elec-ua) for [ru](http://ru.cloudcmd.io "Cloud Commander in Russian") and [ua](http://ua.cloudcmd.io "Cloud Commander in Ukrainian") translations. diff --git a/cloudcmd.js b/cloudcmd.js index 0f20695d..65d35322 100644 --- a/cloudcmd.js +++ b/cloudcmd.js @@ -67,7 +67,12 @@ }); if (!Config.appcache) - lData = Util.removeStr(lData, ' manifest="/cloudcmd.appcache"'); + lData = Util.removeStr(lData, [ + /* min */ + ' manifest=/cloudcmd.appcache', + /* normal */ + ' manifest="/cloudcmd.appcache"' + ]); if (!Config.show_keys_panel){ lKeysPanel = '
li::-moz-selection, #right>li::-moz-selection, .path::-moz-selection, .fm-header::-moz-selection, -.mini-icon::-moz-selection, .name::-moz-selection, .size::-moz-selection, .owner::-moz-selection, .mode::-moz-selection { - text-shadow: none; opacity: 0; -} - -#fm::selection, #left>li::selection, #right>li::selection, .path::selection, .fm-header::selection, -.mini-icon::selection, .name::selection, .size::selection, .owner::selection, .mode::selection { - text-shadow: none; - opacity: 0; - background-color:white; /* opera */ +#fm, #left>li, #right>li, .path, .fm-header, +.mini-icon, .name, .size, .owner, .mode, #keyspanel, .cmd-button { + -webkit-user-select : none; + -moz-user-select : none; + -ms-user-select : none; + -o-user-select : none; + user-select : none; } .path-icon { - position: relative; - top: 3px; - left: -4px; - display: inline-block; - /* размер иконки и позиция на png-файле*/ - width: 15px; - height: 15px; - font-family:'FoundationIconsGeneralEnclosed'; - font-size:30px; - color: #46A4C3;/*#55BF3F; green*/ - text-shadow:black 0 2px 1px; + position : relative; + top : 3px; + left : -4px; + display : inline-block; + width : 15px; + height : 15px; + color : #46A4C3;/*#55BF3F; green*/ + text-shadow :black 0 2px 1px; } + .path-icon:hover { cursor:pointer; } + .path-icon:active { position: relative; top: 4px; text-shadow:black 0 0 1px; -} -.icon { - display:inline-block; - width:16px; - height:16px; - margin-left:0.5%; - /* font-family: 'GeneralFoundicons'; */ - font-family: 'Fontello'; } + +.icon { + margin-left : 0.5%; +} + .error::before { position: relative; font-size: 14px; + font-family : 'Fontello'; color: rgb(222, 41, 41); cursor :default; content: '\2757'; } + .loading { + display : inline-block; + width : 15px; + height : 14.8px; position:relative; top:1px; background:url(/img/spinner.gif); } -.error:hover { - color:rgb(222, 41, 41); - color:rgba(222, 41, 41, 0.81); -} - .refresh-icon { background:url(/img/panel_refresh.png) no-repeat; } @@ -99,24 +92,27 @@ body { } .cmd-button { + width: 8%; + margin: 20px 2px 0 2px; + overflow: hidden; + color: rgb(49,123,249); + text-overflow: ellipsis; + white-space: nowrap; background-color: white; border: 1.5px solid rgba(49,123,249,.40); - color: rgb(49,123,249); - margin: 20px 2px 0 2px; - text-overflow: ellipsis; - overflow: hidden; outline: 0; - white-space: nowrap; - width: 8%; + transition: ease 0.1s; } .cmd-button:hover { border: 1.5px solid rgb(0,0,0); + transition: ease 0.5s; } .cmd-button:active { color: white; background-color: rgb(49,123,249); + transition: ease 0.1s; } .clear-cache { @@ -176,55 +172,50 @@ body { #path{ margin-left:1.5%; } -.left, #left{ +.left, #left { float:left; } -/* фон файла, на котором курсор*/ .current-file { - border: 1.5px solid rgba(49, 123, 249, .40); + box-shadow: inset 0 0 2px rgb(49, 123, 249); + transition: ease 0.05s; } -.selected-file, .selected-file .name > a{ +.selected-file, .selected-file .name > a { color: rgb(254,159,224); } -.right, #right{ +.right, #right { float:right; } -.panel{ +.panel { width: 46%; + padding: 20px; + margin: 0; overflow-y: auto; border: 1.5px solid rgba(49, 123, 249, .40); } -#keyspanel{ + +.selected-panel { + border-color: rgba(254, 159, 224, .40); +} + +#keyspanel { text-align: center; } -/* информация о файлах и папках*/ .name { float: left; width: 35%; - /* если длина имени файла больше 16 символов - * отрезаем лишнее, оставляя лишь 16, - * и добавляем две точки и тайтл - */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .size { float:left; width:16%; - /* Ставим отступ, что бы - * size не налазил на uid - * (owner) - */ margin-right: 27px; - /* Ставим выравнивание, - * что бы размер был в - * одной ровной колонке - */ text-align: right; } .mode { @@ -232,23 +223,21 @@ body { width: 23%; } -/* changin ul to panel for high speed parsing*/ -.panel, li{ - list-style-type:none; - /* making cursor just arrow, - * not text editing cursor - */ - cursor:default; +.panel, li { + list-style-type: none; + cursor: default; } -button{ + +button { width:10%; } -a{ + +a { text-decoration:none; } a:hover, a:active { - color: #06e; - cursor:pointer; + color : #06e; + cursor: pointer; } /* Если размер окна очень маленький @@ -256,14 +245,7 @@ a:hover, a:active { * друг-под-другом */ /* responsive design */ -@media only screen and (max-width: 600px){ - .panel{ - width:94% !important; - } - /* если правая панель не помещаеться - прячем её */ - #right{ - display:none; - } +@media only screen and (max-width: 600px) { /* текущий файл под курсором */ .current-file { background-color: rgb(49, 123, 249); @@ -309,7 +291,8 @@ a:hover, a:active { /* убираем заголовок*/ .fm_header { display:none; - } + } + .mode,.size,.owner { /* располагаем элементы * один под другим @@ -352,29 +335,24 @@ a:hover, a:active { width: 20%; } } -@media only screen and (min-width: 601px) and (max-width: 785px){ - .panel{ - width:94% !important; - } - #right{ - display:none; - } - +@media only screen and (min-width: 601px) and (max-width: 785px) { .cmd-button { - width: 15%; + width: 13%; } } -@media only screen and (min-width:786px) and (max-width: 1155px){ - .panel{ - width:94% !important; - } - /* если правая панель не помещаеться - прячем её */ - #right{ - display:none; - } - +@media only screen and (min-width:786px) and (max-width: 1155px) { .cmd-button { width: 10%; } -} \ No newline at end of file +} + +@media only screen and (max-width: 1155px) { + .panel { + width:94%; + } + /* если правая панель не помещаеться - прячем её */ + #right, .cmd-button#f5, .cmd-button#f6 { + display: none; + } +} diff --git a/html/index.html b/html/index.html index d40145bd..7ee9cec4 100644 --- a/html/index.html +++ b/html/index.html @@ -29,11 +29,6 @@
- - \ No newline at end of file + diff --git a/img/screenshot/console.png b/img/screenshot/console.png new file mode 100644 index 00000000..fdc893e6 Binary files /dev/null and b/img/screenshot/console.png differ diff --git a/img/screenshot/edit.png b/img/screenshot/edit.png new file mode 100644 index 00000000..96d5cbc1 Binary files /dev/null and b/img/screenshot/edit.png differ diff --git a/img/screenshot/menu.png b/img/screenshot/menu.png new file mode 100644 index 00000000..1bcec184 Binary files /dev/null and b/img/screenshot/menu.png differ diff --git a/json/config.json b/json/config.json index b012c13a..4b910d41 100644 --- a/json/config.json +++ b/json/config.json @@ -1,6 +1,8 @@ { "api_url" : "/api/v1", "appcache" : false, + "analytics" : true, + "localStorage" : true, "minification" : { "js" : true, "css" : true, diff --git a/lib/client.js b/lib/client.js index ab3e4568..4d18c7ea 100644 --- a/lib/client.js +++ b/lib/client.js @@ -7,8 +7,7 @@ var Util, DOM, CloudFunc, CloudCmd; (function(Util, DOM){ 'use strict'; - var Key, Config, Modules, FileTemplate, PathTemplate, - Events = DOM.Events, + var Key, Config, Modules, FileTemplate, PathTemplate, Listeners, Cache = DOM.Cache; CloudCmd = { @@ -26,16 +25,6 @@ var Util, DOM, CloudFunc, CloudCmd; })() }; - CloudCmd.GoogleAnalytics = function(){ - Events.addOneTime('mousemove', function(){ - var lUrl = CloudCmd.LIBDIRCLIENT + 'google_analytics.js'; - - setTimeout(function(){ - DOM.jsload(lUrl); - }, 5000); - }); - }; - /** * Функция привязываеться ко всем ссылкам и * загружает содержимое каталогов @@ -73,17 +62,12 @@ var Util, DOM, CloudFunc, CloudCmd; * @param pDirName - имя каталога с которого мы пришли */ function currentToParent(pDirName){ + var lRootDir; /* убираем слэш с имени каталога */ - pDirName = Util.removeStr(pDirName, '/'); + pDirName = Util.removeStr(pDirName, '/'); + lRootDir = DOM.getCurrentFileByName(pDirName); - /* опредиляем в какой мы панели: * - * правой или левой */ - var lPanel = DOM.getPanel(), - lRootDir = DOM.getById(pDirName + '(' + lPanel.id + ')'); - - /* if found li element with ID directory name * - * set it to current file */ - if(lRootDir){ + if (lRootDir){ DOM.setCurrentFile(lRootDir); DOM.scrollIntoViewIfNeeded(lRootDir, true); } @@ -104,7 +88,7 @@ var Util, DOM, CloudFunc, CloudCmd; lPath = pParams; if(lPath && !lName){ - lName = lPath[0].toUpperCase() + lPath.substring(1); + lName = Util.getStrBigFirst(lPath); lName = Util.removeStr(lName, '.js'); var lSlash = lName.indexOf('/'); @@ -134,34 +118,67 @@ var Util, DOM, CloudFunc, CloudCmd; * инициализации */ CloudCmd.init = function(){ - var lCallBack = function(){ - Util.loadOnLoad([ - initKeysPanel, - initModules, - baseInit - ]); - }, - lFunc = function(pCallBack){ - CloudCmd.OLD_BROWSER = true; - var lSrc = CloudCmd.LIBDIRCLIENT + 'polyfill.js'; - - DOM.jqueryLoad( - DOM.retJSLoad(lSrc, pCallBack) - ); - }; + var lCallBack, lFunc, lHeight; + /* устанавливаем размер высоты таблицы файлов + * исходя из размеров разрешения экрана + * + * формируем и округляем высоту экрана + * при разрешениии 1024x1280: + * 658 -> 700 + */ + + lHeight = window.screen.height; + lHeight = lHeight - (lHeight/3).toFixed(); + lHeight = (lHeight / 100).toFixed() * 100; + + CloudCmd.HEIGHT = lHeight; + + DOM.cssSet({ + id:'cloudcmd', + inner: + '.panel{' + + 'height:' + lHeight +'px;' + + '}' + }); + + lCallBack = function(){ + Util.loadOnLoad([ + Util.retExec(CloudCmd.route, location.hash), + baseInit, + initModules, + ]); + }, + lFunc = function(pCallBack){ + CloudCmd.OLD_BROWSER = true; + var lSrc = CloudCmd.LIBDIRCLIENT + 'polyfill.js'; + + DOM.jqueryLoad( + DOM.retJSLoad(lSrc, pCallBack) + ); + }; Util.ifExec(document.body.scrollIntoViewIfNeeded, lCallBack, lFunc); + }; + + CloudCmd.route = function(pPath){ + var lQuery, lModule, lFile, lCurrent, lMsg; - DOM.Events.add(['unload', 'beforeunload'], function (pEvent) { - var lRet, lIsBind = Key.isBind(); - - if(!lIsBind) { - DOM.preventDefault(pEvent); - lRet = 'Please make sure that you saved all work.'; + if (pPath.length > 0) { + lQuery = pPath.split('/'); + + if (lQuery.length > 0) { + lModule = Util.getStrBigFirst(lQuery[1]); + lFile = lQuery[2]; + lCurrent = DOM.getCurrentFileByName(lFile); + if (lFile && !lCurrent) { + lMsg = CloudFunc.format('set current file', lFile, 'error'); + Util.log(lMsg); + } else { + DOM.setCurrentFile(lCurrent); + CloudCmd.execFromModule(lModule, 'show'); } - - return lRet; - }); + } + } }; function initModules(pCallBack){ @@ -177,10 +194,6 @@ var Util, DOM, CloudFunc, CloudCmd; CloudCmd.getModules(function(pModules) { pModules = pModules || []; - Events.addContextMenu(function(pEvent){ - CloudCmd.Menu.ENABLED || DOM.preventDefault(pEvent); - }, document); - var i, n, lStorage = 'storage', lShowLoadFunc = Util.retFunc( DOM.Images.showLoad, {top:true} ), @@ -221,75 +234,38 @@ var Util, DOM, CloudFunc, CloudCmd; }); } - function initKeysPanel(pCallBack){ - var i, lButton, lEl, - lKeysPanel = {}, - - lFuncs =[ - null, - CloudCmd.Help, /* f1 */ - DOM.renameCurrent, /* f2 */ - CloudCmd.View, /* f3 */ - CloudCmd.Edit, /* f4 */ - DOM.copyCurrent, /* f5 */ - DOM.moveCurrent, /* f6 */ - DOM.promptNewDir, /* f7 */ - DOM.promptDeleteSelected, /* f8 */ - CloudCmd.Menu, /* f9 */ - ]; - - for (i = 1; i <= 9; i++) { - lButton = 'f' + i, - lEl = DOM.getById('f' + i); - lKeysPanel[lButton] = lEl; - - if (i === 1 || i === 3 || i === 4 || i === 9) - Events.addOneTime('click', lFuncs[i], lEl); - else - Events.addClick(lFuncs[i], lEl); - } - - lButton = '~', - lEl = DOM.getById('~'); - lKeysPanel[lButton] = lEl; - Events.addOneTime('click', CloudCmd.Console, lEl); - - CloudCmd.KeysPanel = lKeysPanel; - Util.exec(pCallBack); - } - function baseInit(pCallBack){ - if(window.applicationCache){ - var lFunc = applicationCache.onupdateready; - - applicationCache.onupdateready = function(){ - Util.log('app cacheed'); - location.reload(); + function baseInit(pCallBack) { + var LIB = CloudCmd.LIBDIR, + LIBCLIENT = CloudCmd.LIBDIRCLIENT, + files = [ + LIB + 'cloudfunc.js', + LIBCLIENT + 'listeners.js' + ]; - Util.exec(lFunc); - }; - } - /* загружаем общие функции для клиента и сервера */ - DOM.jsload(CloudCmd.LIBDIR + 'cloudfunc.js', function(){ - Events.add("popstate", function(pEvent) { - var lPath = pEvent.state + '?json'; - - if(lPath) - ajaxLoad(lPath, {nohistory: true}); - - return true; - }); + DOM.anyLoadInParallel(files, function() { + Listeners = CloudCmd.Listeners; - changeLinks(CloudFunc.LEFTPANEL); - changeLinks(CloudFunc.RIGHTPANEL); + Listeners.init(); + /* загружаем Google Analytics */ + Listeners.analytics(); + Listeners.changeLinks(CloudFunc.LEFTPANEL); + Listeners.changeLinks(CloudFunc.RIGHTPANEL); + + CloudCmd.KeysPanel = Listeners.initKeysPanel(); - /* устанавливаем переменную доступности кэша */ - Cache.setAllowed(true); - /* Устанавливаем кэш корневого каталога */ - var lDirPath = DOM.getCurrentDirPath(); - if( !Cache.get(lDirPath) ) - Cache.set(lDirPath, getJSONfromFileTable()); + CloudCmd.getConfig(function(config) { + var localStorage = config.localStorage; + /* устанавливаем переменную доступности кэша */ + Cache.setAllowed(localStorage); + /* Устанавливаем кэш корневого каталога */ + var lDirPath = DOM.getCurrentDirPath(); + if (!Cache.get(lDirPath)) + Cache.set(lDirPath, getJSONfromFileTable()); + }); + + Util.exec(pCallBack); }); /* выделяем строку с первым файлом */ @@ -302,35 +278,10 @@ var Util, DOM, CloudFunc, CloudCmd; /* показываем элементы, которые будут работать только, если есть js */ var lFM = DOM.getById('fm'); lFM.className='localstorage'; - - /* устанавливаем размер высоты таблицы файлов - * исходя из размеров разрешения экрана - * - * формируем и округляем высоту экрана - * при разрешениии 1024x1280: - * 658 -> 700 - */ - var lHeight = window.screen.height; - lHeight = lHeight - (lHeight/3).toFixed(); - - lHeight = (lHeight / 100).toFixed() * 100; - - CloudCmd.HEIGHT = lHeight; - - DOM.cssSet({ - id:'cloudcmd', - inner: - '.panel{' + - 'height:' + lHeight +'px;' + - '}' - }); - - Util.exec(pCallBack); CloudCmd.Key(); } - function getSystemFile(pGlobal, pURL){ function lGetSysFile(pCallBack){ @@ -354,11 +305,11 @@ var Util, DOM, CloudFunc, CloudCmd; CloudCmd.getConfig = getSystemFile(Config, CloudCmd.JSONDIR + 'config.json'); CloudCmd.getModules = getSystemFile(Modules, CloudCmd.JSONDIR + 'modules.json'); CloudCmd.getFileTemplate = getSystemFile(FileTemplate, CloudCmd.HTMLDIR + 'file.html'); - CloudCmd.getpPathTemplate = getSystemFile(PathTemplate, CloudCmd.HTMLDIR + 'path.html'); + CloudCmd.getPathTemplate = getSystemFile(PathTemplate, CloudCmd.HTMLDIR + 'path.html'); CloudCmd.execFromModule = function(pModuleName, pFuncName, pParams){ var lObject = CloudCmd[pModuleName]; - Util.ifExec('init' in lObject, + Util.ifExec(Util.isObject(lObject), function(){ var lObj = CloudCmd[pModuleName]; Util.exec( lObj[pFuncName], pParams); @@ -369,132 +320,16 @@ var Util, DOM, CloudFunc, CloudCmd; }); }; - - /** - * функция меняет ссыки на ajax-овые - * @param pPanelID - */ - function changeLinks(pPanelID){ - /* назначаем кнопку очистить кэш и показываем её */ - var lClearcache = DOM.getById('clear-cache'); - Events.addClick(Cache.clear, lClearcache); - - /* меняем ссылки на ajax-запросы */ - var lPanel = DOM.getById(pPanelID), - a = DOM.getByTag('a', lPanel), - - /* right mouse click function varible */ - lOnContextMenu_f = function(pEvent){ - var lReturn_b = true; - - Key && Key.unsetBind(); - - /* getting html element - * currentTarget - DOM event - * target - jquery event - */ - var lTarget = pEvent.currentTarget || pEvent.target; - DOM.setCurrentFile(lTarget); - - if(Util.isFunction(CloudCmd.Menu) ){ - CloudCmd.Menu({ - x: pEvent.clientX, - y: pEvent.clientY - }); - - /* disabling browsers menu*/ - lReturn_b = false; - DOM.Images.showLoad(); - } - - return lReturn_b; - }, - - /* drag and drop function varible - * download file from browser to descktop - * in Chrome (HTML5) - */ - lOnDragStart_f = function(pEvent){ - var lElement = pEvent.target, - lLink = lElement.href, - lName = lElement.textContent; - - /* if it's directory - adding json extension */ - if( DOM.isCurrentIsDir() ){ - lName += '.json'; - lLink += '?json'; - } - - pEvent.dataTransfer.setData("DownloadURL", - 'application/octet-stream' + ':' + - lName + ':' + - lLink); - }, - - lSetCurrentFile_f = function(pEvent){ - var pElement = pEvent.target, - lTag = pElement.tagName; - - if(lTag !== 'LI') - do{ - pElement = pElement.parentElement; - lTag = pElement.tagName; - }while(lTag !== 'LI'); - - DOM.setCurrentFile(pElement); - }, - - lUrl = CloudCmd.HOST, - lLoadDirOnce = CloudCmd.loadDir(); - - CloudCmd.refresh = function(pCurrent){ - var lNEEDREFRESH = true, - lPanel = pCurrent && pCurrent.parentElement, - lPath = DOM.getCurrentDirPath(lPanel), - lLink = CloudFunc.FS + lPath, - lNotSlashlLink = CloudFunc.removeLastSlash(lLink), - lLoad = CloudCmd.loadDir(lNotSlashlLink, lNEEDREFRESH); - lLoad(); - }; + CloudCmd.refresh = function(pCurrent){ + var lNEEDREFRESH = true, + lPanel = pCurrent && pCurrent.parentElement, + lPath = DOM.getCurrentDirPath(lPanel), + lLink = CloudFunc.FS + lPath, + lNotSlashlLink = CloudFunc.removeLastSlash(lLink), + lLoad = CloudCmd.loadDir(lNotSlashlLink, lNEEDREFRESH); - /* ставим загрузку гифа на клик*/ - Events.addClick( CloudCmd.refresh, a[0].parentElement ); - - /* start from 1 cous 0 is a path and it's setted up */ - for(var i = 1, n = a.length; i < n ; i++){ - /* убираем адрес хоста*/ - var ai = a[i], - lLink = Util.removeStr(ai.href, lUrl), - lLoadDir = CloudCmd.loadDir(lLink), - /* устанавливаем обработчики на строку - * на двойное нажатие на левую кнопку мышки */ - lLi = ai.parentElement.parentElement; - - /* if we in path - set click event */ - if (lLi.className === 'path') - Events.addClick( lLoadDir, ai ); - else { - Events.add({ - 'click' : DOM.preventDefault, - 'mousedown' : lSetCurrentFile_f, - 'contextmenu' : lOnContextMenu_f - }, lLi); - - Events.add('dragstart', lOnDragStart_f, ai); - - /* если ссылка на папку, а не файл */ - if(ai.target !== '_blank'){ - Events.add({ - 'dblclick' : lLoadDirOnce, - 'touchend' : lLoadDirOnce - }, lLi); - } - - lLi.id = (ai.title ? ai.title : ai.textContent) + - '(' + pPanelID + ')'; - } - } - } + lLoad(); + }; /** * Функция загружает json-данные о Файловой Системе @@ -511,7 +346,7 @@ var Util, DOM, CloudFunc, CloudCmd; var lSLASH = '/', lFSPath = decodeURI(pPath), lNOJSPath = Util.removeStr( lFSPath, '?json' ), - lCleanPath = Util.removeStr( lNOJSPath, CloudFunc.FS ) || lSLASH, + lCleanPath = Util.removeStrOneTime( lNOJSPath, CloudFunc.FS ) || lSLASH, lOldURL = window.location.pathname; @@ -549,6 +384,8 @@ var Util, DOM, CloudFunc, CloudCmd; DOM.getCurrentFileContent({ url : lFSPath, + dataType: 'json', + error : function(){ DOM.setHistory(lOldURL, null, lOldURL); }, @@ -583,46 +420,48 @@ var Util, DOM, CloudFunc, CloudCmd; lDir = DOM.getCurrentDirName(), lName = DOM.getCurrentName(lCurrent), - lWasRefresh_b = lPath[0].textContent === pJSON[0].path; + lWasRefresh_b = lPath[0].textContent === pJSON[0].path, + lFuncs = [ + CloudCmd.getFileTemplate, + CloudCmd.getPathTemplate + ]; - CloudCmd.getFileTemplate(function(pTemplate){ - CloudCmd.getpPathTemplate(function(pPathTemplate){ - /* очищаем панель */ - var i = lElem.childNodes.length; - - while(i--) - lElem.removeChild(lElem.lastChild); - - lElem.innerHTML = CloudFunc.buildFromJSON(pJSON, pTemplate, pPathTemplate); - - /* если нажали на ссылку на верхний каталог*/ - var lFound; - /* searching current file */ - if(lWasRefresh_b){ - var n = lElem.childNodes.length; - for(i = 2; i < n ; i++){ - var lVarCurrent = lElem.childNodes[i], - lVarName = DOM.getCurrentName(lVarCurrent); - - lFound = lVarName === lName; - - if(lFound){ - lCurrent = lElem.childNodes[i]; - break; - } + Util.asyncCall(lFuncs, function(pTemplate, pPathTemplate) { + /* очищаем панель */ + var i = lElem.childNodes.length; + + while(i--) + lElem.removeChild(lElem.lastChild); + + lElem.innerHTML = CloudFunc.buildFromJSON(pJSON, pTemplate, pPathTemplate); + + /* если нажали на ссылку на верхний каталог*/ + var lFound; + /* searching current file */ + if(lWasRefresh_b){ + var n = lElem.childNodes.length; + for(i = 2; i < n ; i++){ + var lVarCurrent = lElem.childNodes[i], + lVarName = DOM.getCurrentName(lVarCurrent); + + lFound = lVarName === lName; + + if(lFound){ + lCurrent = lElem.childNodes[i]; + break; } } - if(!lFound) /* .. */ - lCurrent = lElem.childNodes[2]; - - DOM.setCurrentFile(lCurrent); - - changeLinks(pElem); - - if(lName === '..' && lDir !== '/') - currentToParent(lDir); - }); - }); + } + if(!lFound) /* .. */ + lCurrent = lElem.childNodes[2]; + + DOM.setCurrentFile(lCurrent); + + Listeners.changeLinks(pElem); + + if(lName === '..' && lDir !== '/') + currentToParent(lDir); + }); } /** @@ -666,11 +505,5 @@ var Util, DOM, CloudFunc, CloudCmd; return Util.stringifyJSON(lFileTable); } - Events.addOneTime('load', function(){ - /* базовая инициализация*/ - CloudCmd.init(); - - /* загружаем Google Analytics */ - CloudCmd.GoogleAnalytics(); - }); + DOM.Events.addOneTime('load', CloudCmd.init); })(Util, DOM); diff --git a/lib/client/google_analytics.js b/lib/client/analytics.js similarity index 87% rename from lib/client/google_analytics.js rename to lib/client/analytics.js index 32238497..38acb0ce 100644 --- a/lib/client/google_analytics.js +++ b/lib/client/analytics.js @@ -1,5 +1,6 @@ var DOM, _gaq; -(function(DOM, _gaq){ + +(function(DOM, _gaq) { 'use strict'; /* setting google analitics tracking code */ @@ -12,4 +13,4 @@ var DOM, _gaq; DOM.jsload('//google-analytics.com/ga.js'); -})(DOM, _gaq); \ No newline at end of file +})(DOM, _gaq); diff --git a/lib/client/console.js b/lib/client/console.js index 32d6a744..7a801325 100644 --- a/lib/client/console.js +++ b/lib/client/console.js @@ -6,6 +6,7 @@ var CloudCmd, Util, DOM, $; function ConsoleProto(CloudCmd, Util, DOM){ var Name = 'Console', + Loading, jqconsole, Element, MouseBinded, @@ -16,6 +17,8 @@ var CloudCmd, Util, DOM, $; this.init = function(pCallBack) { var lFunc, lIsFunc = Util.isFunction(CloudCmd.View); + Loading = true; + if (lIsFunc) lFunc = CloudCmd.View; else @@ -36,65 +39,67 @@ var CloudCmd, Util, DOM, $; }; this.show = function() { - Images.showLoad({top:true}); - - if (!Element) { - Element = DOM.anyload({ - name : 'div', - className : 'console' - }); + if (!Loading) { + Images.showLoad({top:true}); - jqconsole = $(Element).jqconsole('', '> '); - // Abort prompt on Ctrl+Z. - jqconsole.RegisterShortcut('Z', function() { - jqconsole.AbortPrompt(); - handler(); - }); - - // Handle a command. - var handler = function(command) { - var lSocket = CloudCmd.Socket; - if (command) { - Images.showLoad({ top:true }); - - if(lSocket) - lSocket.send(command); - } - - jqconsole.Prompt(true, handler); - }; - - // Initiate the first prompt. - handler(); - } - - CloudCmd.View.show(Element, function(){ - var l$Console = jqconsole.$console, - l$Input = jqconsole.$input_source, - lFocus = function(){ - var x = window.scrollX, - y = window.scrollY; - - l$Input.focus(); - window.scrollTo(x,y); - }; - - lFocus(); - - if (!MouseBinded) { - MouseBinded = true; - - $(l$Console).unbind('mouseup'); - $(l$Console).mouseup(function() { - if( !window.getSelection().toString() ) { - var lTop = l$Console.scrollTop(); - - lFocus(); - l$Console.scrollTop(lTop); - } + if (!Element) { + Element = DOM.anyload({ + name : 'div', + className : 'console' }); + + jqconsole = $(Element).jqconsole('', '> '); + // Abort prompt on Ctrl+Z. + jqconsole.RegisterShortcut('Z', function() { + jqconsole.AbortPrompt(); + handler(); + }); + + // Handle a command. + var handler = function(command) { + var lSocket = CloudCmd.Socket; + if (command) { + Images.showLoad({ top:true }); + + if(lSocket) + lSocket.send(command); + } + + jqconsole.Prompt(true, handler); + }; + + // Initiate the first prompt. + handler(); } - }); + + CloudCmd.View.show(Element, function(){ + var l$Console = jqconsole.$console, + l$Input = jqconsole.$input_source, + lFocus = function(){ + var x = window.scrollX, + y = window.scrollY; + + l$Input.focus(); + window.scrollTo(x,y); + }; + + lFocus(); + + if (!MouseBinded) { + MouseBinded = true; + + $(l$Console).unbind('mouseup'); + $(l$Console).mouseup(function() { + if( !window.getSelection().toString() ) { + var lTop = l$Console.scrollTop(); + + lFocus(); + l$Console.scrollTop(lTop); + } + }); + } + }); + } }; this.hide = function(){ @@ -129,12 +134,12 @@ var CloudCmd, Util, DOM, $; lFiles = [ lDir + 'jqconsole.js', lDir + 'jqconsole.css', - lDir + 'ansi.css', - lDir + 'jquery-migrate-1.2.1.js' + lDir + 'ansi.css' ]; DOM.anyLoadInParallel(lFiles, function(){ - console.timeEnd(Name + ' load'); + Util.timeEnd(Name + ' load'); + Loading = false; Util.exec(pCallBack); }); diff --git a/lib/client/console/jqconsole.js b/lib/client/console/jqconsole.js index dc1c0bfb..0561b911 100644 --- a/lib/client/console/jqconsole.js +++ b/lib/client/console/jqconsole.js @@ -4,7 +4,69 @@ Copyrights 2011, the repl.it project. Licensed under the MIT license */ +// Limit scope pollution from any deprecated API +(function() { + var matched, browser; + +// Use of jQuery.browser is frowned upon. +// More details: http://api.jquery.com/jQuery.browser +// jQuery.uaMatch maintained for back-compat + jQuery.uaMatch = function( ua ) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || + /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + + matched = jQuery.uaMatch( navigator.userAgent ); + browser = {}; + + if ( matched.browser ) { + browser[ matched.browser ] = true; + browser.version = matched.version; + } + +// Chrome is Webkit, but Webkit is also Safari. + if ( browser.chrome ) { + browser.webkit = true; + } else if ( browser.webkit ) { + browser.safari = true; + } + + jQuery.browser = browser; + + jQuery.sub = function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }; + +})(); (function() { var $, Ansi, CLASS_ANSI, CLASS_BLURRED, CLASS_CURSOR, CLASS_HEADER, CLASS_INPUT, CLASS_OLD_PROMPT, CLASS_PREFIX, CLASS_PROMPT, DEFAULT_INDENT_WIDTH, DEFAULT_PROMPT_CONINUE_LABEL, DEFAULT_PROMPT_LABEL, EMPTY_DIV, EMPTY_SELECTOR, EMPTY_SPAN, ESCAPE_CHAR, ESCAPE_SYNTAX, E_KEYPRESS, JQConsole, KEY_BACKSPACE, KEY_DELETE, KEY_DOWN, KEY_END, KEY_ENTER, KEY_HOME, KEY_LEFT, KEY_PAGE_DOWN, KEY_PAGE_UP, KEY_RIGHT, KEY_TAB, KEY_UP, NEWLINE, STATE_INPUT, STATE_OUTPUT, STATE_PROMPT, spanHtml, diff --git a/lib/client/console/jquery-migrate-1.2.1.js b/lib/client/console/jquery-migrate-1.2.1.js deleted file mode 100644 index cf3e74c2..00000000 --- a/lib/client/console/jquery-migrate-1.2.1.js +++ /dev/null @@ -1,521 +0,0 @@ -/*! - * jQuery Migrate - v1.2.1 - 2013-05-08 - * https://github.com/jquery/jquery-migrate - * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT - */ -(function( jQuery, window, undefined ) { -// See http://bugs.jquery.com/ticket/13335 -// "use strict"; - - -var warnedAbout = {}; - -// List of warnings already given; public read only -jQuery.migrateWarnings = []; - -// Set to true to prevent console output; migrateWarnings still maintained -// jQuery.migrateMute = false; - -// Show a message on the console so devs know we're active -if ( !jQuery.migrateMute && window.console && window.console.log ) { - window.console.log("JQMIGRATE: Logging is active"); -} - -// Set to false to disable traces that appear with warnings -if ( jQuery.migrateTrace === undefined ) { - jQuery.migrateTrace = true; -} - -// Forget any warnings we've already given; public -jQuery.migrateReset = function() { - warnedAbout = {}; - jQuery.migrateWarnings.length = 0; -}; - -function migrateWarn( msg) { - var console = window.console; - if ( !warnedAbout[ msg ] ) { - warnedAbout[ msg ] = true; - jQuery.migrateWarnings.push( msg ); - if ( console && console.warn && !jQuery.migrateMute ) { - console.warn( "JQMIGRATE: " + msg ); - if ( jQuery.migrateTrace && console.trace ) { - console.trace(); - } - } - } -} - -function migrateWarnProp( obj, prop, value, msg ) { - if ( Object.defineProperty ) { - // On ES5 browsers (non-oldIE), warn if the code tries to get prop; - // allow property to be overwritten in case some other plugin wants it - try { - Object.defineProperty( obj, prop, { - configurable: true, - enumerable: true, - get: function() { - migrateWarn( msg ); - return value; - }, - set: function( newValue ) { - migrateWarn( msg ); - value = newValue; - } - }); - return; - } catch( err ) { - // IE8 is a dope about Object.defineProperty, can't warn there - } - } - - // Non-ES5 (or broken) browser; just set the property - jQuery._definePropertyBroken = true; - obj[ prop ] = value; -} - -if ( document.compatMode === "BackCompat" ) { - // jQuery has never supported or tested Quirks Mode - migrateWarn( "jQuery is not compatible with Quirks Mode" ); -} - - -var attrFn = jQuery( "", { size: 1 } ).attr("size") && jQuery.attrFn, - oldAttr = jQuery.attr, - valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get || - function() { return null; }, - valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set || - function() { return undefined; }, - rnoType = /^(?:input|button)$/i, - rnoAttrNodeType = /^[238]$/, - rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, - ruseDefault = /^(?:checked|selected)$/i; - -// jQuery.attrFn -migrateWarnProp( jQuery, "attrFn", attrFn || {}, "jQuery.attrFn is deprecated" ); - -jQuery.attr = function( elem, name, value, pass ) { - var lowerName = name.toLowerCase(), - nType = elem && elem.nodeType; - - if ( pass ) { - // Since pass is used internally, we only warn for new jQuery - // versions where there isn't a pass arg in the formal params - if ( oldAttr.length < 4 ) { - migrateWarn("jQuery.fn.attr( props, pass ) is deprecated"); - } - if ( elem && !rnoAttrNodeType.test( nType ) && - (attrFn ? name in attrFn : jQuery.isFunction(jQuery.fn[name])) ) { - return jQuery( elem )[ name ]( value ); - } - } - - // Warn if user tries to set `type`, since it breaks on IE 6/7/8; by checking - // for disconnected elements we don't warn on $( "\ + \ \ \
\ @@ -244,7 +244,7 @@ var SearchBox = function(editor, range, showReplaceForm) { event.stopPropagation(e); }); event.addListener(sb, "click", function(e) { - var t = e.target; + var t = e.target || e.srcElement; var action = t.getAttribute("action"); if (action && _this[action]) _this[action](); @@ -369,10 +369,18 @@ var SearchBox = function(editor, range, showReplaceForm) { this.find(true, true); }; this.replace = function() { - this.editor.replace(this.replaceInput.value); + if (!this.editor.getReadOnly()) + this.editor.replace(this.replaceInput.value); + }; + this.replaceAndFindNext = function() { + if (!this.editor.getReadOnly()) { + this.editor.replace(this.replaceInput.value); + this.findNext() + } }; this.replaceAll = function() { - this.editor.replaceAll(this.replaceInput.value); + if (!this.editor.getReadOnly()) + this.editor.replaceAll(this.replaceInput.value); }; this.hide = function() { @@ -403,45 +411,4 @@ exports.Search = function(editor, isReplace) { sb.show(editor.session.getTextRange(), isReplace); }; - -exports.ISearch = function(session, options) { - this.$changeListener = this.$changeListener.bind(this); - this.startRange = session.selection.toOrientedRange(); - this.options = options || {}; -}; - -(function(){ - this.setSession = function(session) { - if (this.session) { - this.session.removeListener(this.$changeListener); - } - this.session = session; - this.session.addListener(this.$changeListener); - }; - this.setSearchString = function() { - - }; - this.getValue = function() { - if (this.value == null) - this.value = this.session.getValue(); - return this.value; - }; - this.$changeListener = function() { - this.value = null; - }; - this.find = function() { - - }; - this.$edgeBefore = function() { - this.cursor = this.startRange[this.options.backwards ? "start" : "end"]; - }; - this.$edgeAfter = function() { - - }; - this.next = function(dir) { - - }; -}).call(exports.ISearch.prototype); - - }); diff --git a/lib/client/edit/mode-javascript.js b/lib/client/edit/mode-javascript.js index 5bfc25dd..bf541754 100644 --- a/lib/client/edit/mode-javascript.js +++ b/lib/client/edit/mode-javascript.js @@ -28,7 +28,7 @@ * * ***** END LICENSE BLOCK ***** */ -define('ace/mode/javascript', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text', 'ace/tokenizer', 'ace/mode/javascript_highlight_rules', 'ace/mode/matching_brace_outdent', 'ace/range', 'ace/worker/worker_client', 'ace/mode/behaviour/cstyle', 'ace/mode/folding/cstyle'], function(require, exports, module) { +ace.define('ace/mode/javascript', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text', 'ace/tokenizer', 'ace/mode/javascript_highlight_rules', 'ace/mode/matching_brace_outdent', 'ace/range', 'ace/worker/worker_client', 'ace/mode/behaviour/cstyle', 'ace/mode/folding/cstyle'], function(require, exports, module) { var oop = require("../lib/oop"); @@ -42,9 +42,12 @@ var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour; var CStyleFoldMode = require("./folding/cstyle").FoldMode; var Mode = function() { - this.$tokenizer = new Tokenizer(new JavaScriptHighlightRules().getRules()); + var highlighter = new JavaScriptHighlightRules(); + + this.$tokenizer = new Tokenizer(highlighter.getRules()); this.$outdent = new MatchingBraceOutdent(); this.$behaviour = new CstyleBehaviour(); + this.$keywordList = highlighter.$keywordList; this.foldingRules = new CStyleFoldMode(); }; oop.inherits(Mode, TextMode); @@ -114,7 +117,7 @@ oop.inherits(Mode, TextMode); exports.Mode = Mode; }); -define('ace/mode/javascript_highlight_rules', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/doc_comment_highlight_rules', 'ace/mode/text_highlight_rules'], function(require, exports, module) { +ace.define('ace/mode/javascript_highlight_rules', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/doc_comment_highlight_rules', 'ace/mode/text_highlight_rules'], function(require, exports, module) { var oop = require("../lib/oop"); @@ -163,7 +166,8 @@ var JavaScriptHighlightRules = function() { "no_regex" : [ { token : "comment", - regex : /\/\/.*$/ + regex : "\\/\\/", + next : "line_comment" }, DocCommentHighlightRules.getStartRule("doc-start"), { @@ -238,7 +242,7 @@ var JavaScriptHighlightRules = function() { next : "start" }, { token : ["punctuation.operator", "support.function"], - regex : /(\.)(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:opzzzz|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/ + regex : /(\.)(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/ }, { token : ["punctuation.operator", "support.function.dom"], regex : /(\.)(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/ @@ -283,8 +287,8 @@ var JavaScriptHighlightRules = function() { next : "comment_regex_allowed" }, { token : "comment", - regex : "\\/\\/.*$", - next : "start" + regex : "\\/\\/", + next : "line_comment_regex_allowed" }, { token: "string.regexp", regex: "\\/", @@ -371,6 +375,14 @@ var JavaScriptHighlightRules = function() { {token : "comment", regex : "\\*\\/", next : "no_regex"}, {defaultToken : "comment"} ], + "line_comment_regex_allowed" : [ + {token : "comment", regex : "$|^", next : "start"}, + {defaultToken : "comment"} + ], + "line_comment" : [ + {token : "comment", regex : "$|^", next : "no_regex"}, + {defaultToken : "comment"} + ], "qqstring" : [ { token : "constant.language.escape", @@ -414,7 +426,7 @@ oop.inherits(JavaScriptHighlightRules, TextHighlightRules); exports.JavaScriptHighlightRules = JavaScriptHighlightRules; }); -define('ace/mode/doc_comment_highlight_rules', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text_highlight_rules'], function(require, exports, module) { +ace.define('ace/mode/doc_comment_highlight_rules', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text_highlight_rules'], function(require, exports, module) { var oop = require("../lib/oop"); @@ -458,7 +470,7 @@ exports.DocCommentHighlightRules = DocCommentHighlightRules; }); -define('ace/mode/matching_brace_outdent', ['require', 'exports', 'module' , 'ace/range'], function(require, exports, module) { +ace.define('ace/mode/matching_brace_outdent', ['require', 'exports', 'module' , 'ace/range'], function(require, exports, module) { var Range = require("../range").Range; @@ -498,7 +510,7 @@ var MatchingBraceOutdent = function() {}; exports.MatchingBraceOutdent = MatchingBraceOutdent; }); -define('ace/mode/behaviour/cstyle', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/behaviour', 'ace/token_iterator', 'ace/lib/lang'], function(require, exports, module) { +ace.define('ace/mode/behaviour/cstyle', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/behaviour', 'ace/token_iterator', 'ace/lib/lang'], function(require, exports, module) { var oop = require("../../lib/oop"); @@ -821,7 +833,7 @@ oop.inherits(CstyleBehaviour, Behaviour); exports.CstyleBehaviour = CstyleBehaviour; }); -define('ace/mode/folding/cstyle', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/range', 'ace/mode/folding/fold_mode'], function(require, exports, module) { +ace.define('ace/mode/folding/cstyle', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/range', 'ace/mode/folding/fold_mode'], function(require, exports, module) { var oop = require("../../lib/oop"); @@ -873,4 +885,4 @@ oop.inherits(FoldMode, BaseFoldMode); }).call(FoldMode.prototype); -}); \ No newline at end of file +}); diff --git a/lib/client/edit/package.txt b/lib/client/edit/package.txt new file mode 100644 index 00000000..7b88b7b5 --- /dev/null +++ b/lib/client/edit/package.txt @@ -0,0 +1,2 @@ +package 07.31.2013 +https://github.com/ajaxorg/ace-builds/tree/995de31e6513ddfd5e9f66b8216f5b316fcd2253 \ No newline at end of file diff --git a/lib/client/edit/theme-tomorrow_night_blue.js b/lib/client/edit/theme-tomorrow_night_blue.js index 86c6e45f..30e1146e 100644 --- a/lib/client/edit/theme-tomorrow_night_blue.js +++ b/lib/client/edit/theme-tomorrow_night_blue.js @@ -28,7 +28,7 @@ * * ***** END LICENSE BLOCK ***** */ -define('ace/theme/tomorrow_night_blue', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { +ace.define('ace/theme/tomorrow_night_blue', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) { exports.isDark = true; exports.cssClass = "ace-tomorrow-night-blue"; @@ -119,7 +119,7 @@ color: #BBDAFF\ .ace-tomorrow-night-blue .ace_support.ace_type {\ color: #FFEEAD\ }\ -.ace-tomorrow-night-blue .ace_markup.ace_heading,\ +.ace-tomorrow-night-blue .ace_heading,\ .ace-tomorrow-night-blue .ace_string {\ color: #D1F1A9\ }\ @@ -133,13 +133,10 @@ color: #FF9DA4\ .ace-tomorrow-night-blue .ace_comment {\ color: #7285B7\ }\ -.ace-tomorrow-night-blue .ace_markup.ace_underline {\ -text-decoration: underline\ -}\ .ace-tomorrow-night-blue .ace_indent-guide {\ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYJDzqfwPAANXAeNsiA+ZAAAAAElFTkSuQmCC) right repeat-y;\ }"; var dom = require("../lib/dom"); dom.importCssString(exports.cssText, exports.cssClass); -}); \ No newline at end of file +}); diff --git a/lib/client/edit/worker-javascript.js b/lib/client/edit/worker-javascript.js index 95aee706..8beac737 100644 --- a/lib/client/edit/worker-javascript.js +++ b/lib/client/edit/worker-javascript.js @@ -4,16 +4,15 @@ if (typeof window.window != "undefined" && window.document) { return; } -window.console = { - log: function() { - var msgs = Array.prototype.slice.call(arguments, 0); - postMessage({type: "log", data: msgs}); - }, - error: function() { - var msgs = Array.prototype.slice.call(arguments, 0); - postMessage({type: "log", data: msgs}); - } +window.console = function() { + var msgs = Array.prototype.slice.call(arguments, 0); + postMessage({type: "log", data: msgs}); }; +window.console.error = +window.console.warn = +window.console.log = +window.console.trace = window.console; + window.window = window; window.ace = window; @@ -86,10 +85,9 @@ window.define = function(id, deps, factory) { }; require.modules[id] = { + exports: {}, factory: function() { - var module = { - exports: {} - }; + var module = this; var returnExports = factory(req, module.exports, module); if (returnExports) module.exports = returnExports; @@ -158,7 +156,7 @@ window.onmessage = function(e) { }; })(this); -define('ace/lib/event_emitter', ['require', 'exports', 'module' ], function(require, exports, module) { +ace.define('ace/lib/event_emitter', ['require', 'exports', 'module' ], function(require, exports, module) { var EventEmitter = {}; @@ -283,7 +281,7 @@ exports.EventEmitter = EventEmitter; }); -define('ace/lib/oop', ['require', 'exports', 'module' ], function(require, exports, module) { +ace.define('ace/lib/oop', ['require', 'exports', 'module' ], function(require, exports, module) { exports.inherits = (function() { @@ -309,7 +307,7 @@ exports.implement = function(proto, mixin) { }); -define('ace/lib/es5-shim', ['require', 'exports', 'module' ], function(require, exports, module) { +ace.define('ace/lib/es5-shim', ['require', 'exports', 'module' ], function(require, exports, module) { function Empty() {} @@ -1006,7 +1004,7 @@ var toObject = function (o) { }); -define('ace/mode/javascript_worker', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/worker/mirror', 'ace/mode/javascript/jshint'], function(require, exports, module) { +ace.define('ace/mode/javascript_worker', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/worker/mirror', 'ace/mode/javascript/jshint'], function(require, exports, module) { var oop = require("../lib/oop"); @@ -1055,8 +1053,8 @@ oop.inherits(JavaScriptWorker, Mirror); (function() { this.setOptions = function(options) { this.options = options || { - es5: true, esnext: true, + moz: true, devel: true, browser: true, node: true, @@ -1153,7 +1151,7 @@ oop.inherits(JavaScriptWorker, Mirror); }).call(JavaScriptWorker.prototype); }); -define('ace/worker/mirror', ['require', 'exports', 'module' , 'ace/document', 'ace/lib/lang'], function(require, exports, module) { +ace.define('ace/worker/mirror', ['require', 'exports', 'module' , 'ace/document', 'ace/lib/lang'], function(require, exports, module) { var Document = require("../document").Document; @@ -1196,7 +1194,7 @@ var Mirror = exports.Mirror = function(sender) { }); -define('ace/document', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter', 'ace/range', 'ace/anchor'], function(require, exports, module) { +ace.define('ace/document', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter', 'ace/range', 'ace/anchor'], function(require, exports, module) { var oop = require("./lib/oop"); @@ -1282,7 +1280,7 @@ var Document = function(text) { }; this.getTextRange = function(range) { if (range.start.row == range.end.row) { - return this.$lines[range.start.row] + return this.getLine(range.start.row) .substring(range.start.column, range.end.column); } var lines = this.getLines(range.start.row, range.end.row); @@ -1548,7 +1546,7 @@ var Document = function(text) { exports.Document = Document; }); -define('ace/range', ['require', 'exports', 'module' ], function(require, exports, module) { +ace.define('ace/range', ['require', 'exports', 'module' ], function(require, exports, module) { var comparePoints = function(p1, p2) { return p1.row - p2.row || p1.column - p2.column; @@ -1787,36 +1785,31 @@ Range.comparePoints = function(p1, p2) { exports.Range = Range; }); -define('ace/anchor', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter'], function(require, exports, module) { +ace.define('ace/anchor', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter'], function(require, exports, module) { var oop = require("./lib/oop"); var EventEmitter = require("./lib/event_emitter").EventEmitter; var Anchor = exports.Anchor = function(doc, row, column) { - this.document = doc; - + this.$onChange = this.onChange.bind(this); + this.attach(doc); + if (typeof column == "undefined") this.setPosition(row.row, row.column); else this.setPosition(row, column); - - this.$onChange = this.onChange.bind(this); - doc.on("change", this.$onChange); }; (function() { oop.implement(this, EventEmitter); - this.getPosition = function() { return this.$clipPositionToDocument(this.row, this.column); }; - this.getDocument = function() { return this.document; }; - this.onChange = function(e) { var delta = e.data; var range = delta.range; @@ -1878,7 +1871,6 @@ var Anchor = exports.Anchor = function(doc, row, column) { this.setPosition(row, column, true); }; - this.setPosition = function(row, column, noClip) { var pos; if (noClip) { @@ -1905,10 +1897,13 @@ var Anchor = exports.Anchor = function(doc, row, column) { value: pos }); }; - this.detach = function() { this.document.removeEventListener("change", this.$onChange); }; + this.attach = function(doc) { + this.document = doc || this.document; + this.document.on("change", this.$onChange); + }; this.$clipPositionToDocument = function(row, column) { var pos = {}; @@ -1935,7 +1930,7 @@ var Anchor = exports.Anchor = function(doc, row, column) { }); -define('ace/lib/lang', ['require', 'exports', 'module' ], function(require, exports, module) { +ace.define('ace/lib/lang', ['require', 'exports', 'module' ], function(require, exports, module) { exports.stringReverse = function(string) { @@ -2111,13 +2106,1089 @@ exports.delayedCall = function(fcn, defaultTimeout) { return _self; }; }); -define('ace/mode/javascript/jshint', ['require', 'exports', 'module' ], function(require, exports, module) { +ace.define('ace/mode/javascript/jshint', ['require', 'exports', 'module' ], function(require, exports, module) { +var require; +require=(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + + return function nextTick(fn) { + queue.push(fn); + window.postMessage('process-tick', '*'); + }; + } + + return function nextTick(fn) { + setTimeout(fn, 0); + }; +})(); + +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +} +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; + +}, +{}], +2:[function(req,module,exports){ +(function(process){if (!process.EventEmitter) process.EventEmitter = function () {}; + +var EventEmitter = exports.EventEmitter = process.EventEmitter; +var isArray = typeof Array.isArray === 'function' + ? Array.isArray + : function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]' + } +; +function indexOf (xs, x) { + if (xs.indexOf) return xs.indexOf(x); + for (var i = 0; i < xs.length; i++) { + if (x === xs[i]) return i; + } + return -1; +} +var defaultMaxListeners = 200; +EventEmitter.prototype.setMaxListeners = function(n) { + if (!this._events) this._events = {}; + this._events.maxListeners = n; +}; + + +EventEmitter.prototype.emit = function(type) { + if (type === 'error') { + if (!this._events || !this._events.error || + (isArray(this._events.error) && !this._events.error.length)) + { + if (arguments[1] instanceof Error) { + throw arguments[1]; // Unhandled 'error' event + } else { + throw new Error("Uncaught, unspecified 'error' event."); + } + return false; + } + } + + if (!this._events) return false; + var handler = this._events[type]; + if (!handler) return false; + + if (typeof handler == 'function') { + switch (arguments.length) { + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + default: + var args = Array.prototype.slice.call(arguments, 1); + handler.apply(this, args); + } + return true; + + } else if (isArray(handler)) { + var args = Array.prototype.slice.call(arguments, 1); + + var listeners = handler.slice(); + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + return true; + + } else { + return false; + } +}; +EventEmitter.prototype.addListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('addListener only takes instances of Function'); + } + + if (!this._events) this._events = {}; + this.emit('newListener', type, listener); + + if (!this._events[type]) { + this._events[type] = listener; + } else if (isArray(this._events[type])) { + if (!this._events[type].warned) { + var m; + if (this._events.maxListeners !== undefined) { + m = this._events.maxListeners; + } else { + m = defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + console.trace(); + } + } + this._events[type].push(listener); + } else { + this._events[type] = [this._events[type], listener]; + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + var self = this; + self.on(type, function g() { + self.removeListener(type, g); + listener.apply(this, arguments); + }); + + return this; +}; + +EventEmitter.prototype.removeListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('removeListener only takes instances of Function'); + } + if (!this._events || !this._events[type]) return this; + + var list = this._events[type]; + + if (isArray(list)) { + var i = indexOf(list, listener); + if (i < 0) return this; + list.splice(i, 1); + if (list.length == 0) + delete this._events[type]; + } else if (this._events[type] === listener) { + delete this._events[type]; + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + if (arguments.length === 0) { + this._events = {}; + return this; + } + if (type && this._events && this._events[type]) this._events[type] = null; + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + if (!this._events) this._events = {}; + if (!this._events[type]) this._events[type] = []; + if (!isArray(this._events[type])) { + this._events[type] = [this._events[type]]; + } + return this._events[type]; +}; + +})(req("__browserify_process")) +}, +{"__browserify_process":1}], +3:[function(req,module,exports){ +(function(){// jshint -W001 + +exports.reservedVars = { + arguments : false, + NaN : false +}; + +exports.ecmaIdentifiers = { + Array : false, + Boolean : false, + Date : false, + decodeURI : false, + decodeURIComponent : false, + encodeURI : false, + encodeURIComponent : false, + Error : false, + "eval" : false, + EvalError : false, + Function : false, + hasOwnProperty : false, + isFinite : false, + isNaN : false, + JSON : false, + Math : false, + Map : false, + Number : false, + Object : false, + parseInt : false, + parseFloat : false, + RangeError : false, + ReferenceError : false, + RegExp : false, + Set : false, + String : false, + SyntaxError : false, + TypeError : false, + URIError : false, + WeakMap : false +}; + +exports.browser = { + ArrayBuffer : false, + ArrayBufferView : false, + Audio : false, + Blob : false, + addEventListener : false, + applicationCache : false, + atob : false, + blur : false, + btoa : false, + clearInterval : false, + clearTimeout : false, + close : false, + closed : false, + DataView : false, + DOMParser : false, + defaultStatus : false, + document : false, + Element : false, + ElementTimeControl : false, + event : false, + FileReader : false, + Float32Array : false, + Float64Array : false, + FormData : false, + focus : false, + frames : false, + getComputedStyle : false, + HTMLElement : false, + HTMLAnchorElement : false, + HTMLBaseElement : false, + HTMLBlockquoteElement: false, + HTMLBodyElement : false, + HTMLBRElement : false, + HTMLButtonElement : false, + HTMLCanvasElement : false, + HTMLDirectoryElement : false, + HTMLDivElement : false, + HTMLDListElement : false, + HTMLFieldSetElement : false, + HTMLFontElement : false, + HTMLFormElement : false, + HTMLFrameElement : false, + HTMLFrameSetElement : false, + HTMLHeadElement : false, + HTMLHeadingElement : false, + HTMLHRElement : false, + HTMLHtmlElement : false, + HTMLIFrameElement : false, + HTMLImageElement : false, + HTMLInputElement : false, + HTMLIsIndexElement : false, + HTMLLabelElement : false, + HTMLLayerElement : false, + HTMLLegendElement : false, + HTMLLIElement : false, + HTMLLinkElement : false, + HTMLMapElement : false, + HTMLMenuElement : false, + HTMLMetaElement : false, + HTMLModElement : false, + HTMLObjectElement : false, + HTMLOListElement : false, + HTMLOptGroupElement : false, + HTMLOptionElement : false, + HTMLParagraphElement : false, + HTMLParamElement : false, + HTMLPreElement : false, + HTMLQuoteElement : false, + HTMLScriptElement : false, + HTMLSelectElement : false, + HTMLStyleElement : false, + HTMLTableCaptionElement: false, + HTMLTableCellElement : false, + HTMLTableColElement : false, + HTMLTableElement : false, + HTMLTableRowElement : false, + HTMLTableSectionElement: false, + HTMLTextAreaElement : false, + HTMLTitleElement : false, + HTMLUListElement : false, + HTMLVideoElement : false, + history : false, + Int16Array : false, + Int32Array : false, + Int8Array : false, + Image : false, + length : false, + localStorage : false, + location : false, + MessageChannel : false, + MessageEvent : false, + MessagePort : false, + moveBy : false, + moveTo : false, + MutationObserver : false, + name : false, + Node : false, + NodeFilter : false, + navigator : false, + onbeforeunload : true, + onblur : true, + onerror : true, + onfocus : true, + onload : true, + onresize : true, + onunload : true, + open : false, + openDatabase : false, + opener : false, + Option : false, + parent : false, + print : false, + removeEventListener : false, + resizeBy : false, + resizeTo : false, + screen : false, + scroll : false, + scrollBy : false, + scrollTo : false, + sessionStorage : false, + setInterval : false, + setTimeout : false, + SharedWorker : false, + status : false, + SVGAElement : false, + SVGAltGlyphDefElement: false, + SVGAltGlyphElement : false, + SVGAltGlyphItemElement: false, + SVGAngle : false, + SVGAnimateColorElement: false, + SVGAnimateElement : false, + SVGAnimateMotionElement: false, + SVGAnimateTransformElement: false, + SVGAnimatedAngle : false, + SVGAnimatedBoolean : false, + SVGAnimatedEnumeration: false, + SVGAnimatedInteger : false, + SVGAnimatedLength : false, + SVGAnimatedLengthList: false, + SVGAnimatedNumber : false, + SVGAnimatedNumberList: false, + SVGAnimatedPathData : false, + SVGAnimatedPoints : false, + SVGAnimatedPreserveAspectRatio: false, + SVGAnimatedRect : false, + SVGAnimatedString : false, + SVGAnimatedTransformList: false, + SVGAnimationElement : false, + SVGCSSRule : false, + SVGCircleElement : false, + SVGClipPathElement : false, + SVGColor : false, + SVGColorProfileElement: false, + SVGColorProfileRule : false, + SVGComponentTransferFunctionElement: false, + SVGCursorElement : false, + SVGDefsElement : false, + SVGDescElement : false, + SVGDocument : false, + SVGElement : false, + SVGElementInstance : false, + SVGElementInstanceList: false, + SVGEllipseElement : false, + SVGExternalResourcesRequired: false, + SVGFEBlendElement : false, + SVGFEColorMatrixElement: false, + SVGFEComponentTransferElement: false, + SVGFECompositeElement: false, + SVGFEConvolveMatrixElement: false, + SVGFEDiffuseLightingElement: false, + SVGFEDisplacementMapElement: false, + SVGFEDistantLightElement: false, + SVGFEFloodElement : false, + SVGFEFuncAElement : false, + SVGFEFuncBElement : false, + SVGFEFuncGElement : false, + SVGFEFuncRElement : false, + SVGFEGaussianBlurElement: false, + SVGFEImageElement : false, + SVGFEMergeElement : false, + SVGFEMergeNodeElement: false, + SVGFEMorphologyElement: false, + SVGFEOffsetElement : false, + SVGFEPointLightElement: false, + SVGFESpecularLightingElement: false, + SVGFESpotLightElement: false, + SVGFETileElement : false, + SVGFETurbulenceElement: false, + SVGFilterElement : false, + SVGFilterPrimitiveStandardAttributes: false, + SVGFitToViewBox : false, + SVGFontElement : false, + SVGFontFaceElement : false, + SVGFontFaceFormatElement: false, + SVGFontFaceNameElement: false, + SVGFontFaceSrcElement: false, + SVGFontFaceUriElement: false, + SVGForeignObjectElement: false, + SVGGElement : false, + SVGGlyphElement : false, + SVGGlyphRefElement : false, + SVGGradientElement : false, + SVGHKernElement : false, + SVGICCColor : false, + SVGImageElement : false, + SVGLangSpace : false, + SVGLength : false, + SVGLengthList : false, + SVGLineElement : false, + SVGLinearGradientElement: false, + SVGLocatable : false, + SVGMPathElement : false, + SVGMarkerElement : false, + SVGMaskElement : false, + SVGMatrix : false, + SVGMetadataElement : false, + SVGMissingGlyphElement: false, + SVGNumber : false, + SVGNumberList : false, + SVGPaint : false, + SVGPathElement : false, + SVGPathSeg : false, + SVGPathSegArcAbs : false, + SVGPathSegArcRel : false, + SVGPathSegClosePath : false, + SVGPathSegCurvetoCubicAbs: false, + SVGPathSegCurvetoCubicRel: false, + SVGPathSegCurvetoCubicSmoothAbs: false, + SVGPathSegCurvetoCubicSmoothRel: false, + SVGPathSegCurvetoQuadraticAbs: false, + SVGPathSegCurvetoQuadraticRel: false, + SVGPathSegCurvetoQuadraticSmoothAbs: false, + SVGPathSegCurvetoQuadraticSmoothRel: false, + SVGPathSegLinetoAbs : false, + SVGPathSegLinetoHorizontalAbs: false, + SVGPathSegLinetoHorizontalRel: false, + SVGPathSegLinetoRel : false, + SVGPathSegLinetoVerticalAbs: false, + SVGPathSegLinetoVerticalRel: false, + SVGPathSegList : false, + SVGPathSegMovetoAbs : false, + SVGPathSegMovetoRel : false, + SVGPatternElement : false, + SVGPoint : false, + SVGPointList : false, + SVGPolygonElement : false, + SVGPolylineElement : false, + SVGPreserveAspectRatio: false, + SVGRadialGradientElement: false, + SVGRect : false, + SVGRectElement : false, + SVGRenderingIntent : false, + SVGSVGElement : false, + SVGScriptElement : false, + SVGSetElement : false, + SVGStopElement : false, + SVGStringList : false, + SVGStylable : false, + SVGStyleElement : false, + SVGSwitchElement : false, + SVGSymbolElement : false, + SVGTRefElement : false, + SVGTSpanElement : false, + SVGTests : false, + SVGTextContentElement: false, + SVGTextElement : false, + SVGTextPathElement : false, + SVGTextPositioningElement: false, + SVGTitleElement : false, + SVGTransform : false, + SVGTransformList : false, + SVGTransformable : false, + SVGURIReference : false, + SVGUnitTypes : false, + SVGUseElement : false, + SVGVKernElement : false, + SVGViewElement : false, + SVGViewSpec : false, + SVGZoomAndPan : false, + TimeEvent : false, + top : false, + Uint16Array : false, + Uint32Array : false, + Uint8Array : false, + Uint8ClampedArray : false, + WebSocket : false, + window : false, + Worker : false, + XMLHttpRequest : false, + XMLSerializer : false, + XPathEvaluator : false, + XPathException : false, + XPathExpression : false, + XPathNamespace : false, + XPathNSResolver : false, + XPathResult : false +}; + +exports.devel = { + alert : false, + confirm: false, + console: false, + Debug : false, + opera : false, + prompt : false +}; + +exports.worker = { + importScripts: true, + postMessage : true, + self : true +}; +exports.nonstandard = { + escape : false, + unescape: false +}; + +exports.couch = { + "require" : false, + respond : false, + getRow : false, + emit : false, + send : false, + start : false, + sum : false, + log : false, + exports : false, + module : false, + provides : false +}; + +exports.node = { + __filename : false, + __dirname : false, + Buffer : false, + DataView : false, + console : false, + exports : true, // In Node it is ok to exports = module.exports = foo(); + GLOBAL : false, + global : false, + module : false, + process : false, + require : false, + setTimeout : false, + clearTimeout : false, + setInterval : false, + clearInterval : false, + setImmediate : false, // v0.9.1+ + clearImmediate: false // v0.9.1+ +}; + +exports.phantom = { + phantom : true, + require : true, + WebPage : true +}; + +exports.rhino = { + defineClass : false, + deserialize : false, + gc : false, + help : false, + importPackage: false, + "java" : false, + load : false, + loadClass : false, + print : false, + quit : false, + readFile : false, + readUrl : false, + runCommand : false, + seal : false, + serialize : false, + spawn : false, + sync : false, + toint32 : false, + version : false +}; + +exports.shelljs = { + target : false, + echo : false, + exit : false, + cd : false, + pwd : false, + ls : false, + find : false, + cp : false, + rm : false, + mv : false, + mkdir : false, + test : false, + cat : false, + sed : false, + grep : false, + which : false, + dirs : false, + pushd : false, + popd : false, + env : false, + exec : false, + chmod : false, + config : false, + error : false, + tempdir : false +}; + +exports.wsh = { + ActiveXObject : true, + Enumerator : true, + GetObject : true, + ScriptEngine : true, + ScriptEngineBuildVersion : true, + ScriptEngineMajorVersion : true, + ScriptEngineMinorVersion : true, + VBArray : true, + WSH : true, + WScript : true, + XDomainRequest : true +}; + +exports.dojo = { + dojo : false, + dijit : false, + dojox : false, + define : false, + "require": false +}; + +exports.jquery = { + "$" : false, + jQuery : false +}; + +exports.mootools = { + "$" : false, + "$$" : false, + Asset : false, + Browser : false, + Chain : false, + Class : false, + Color : false, + Cookie : false, + Core : false, + Document : false, + DomReady : false, + DOMEvent : false, + DOMReady : false, + Drag : false, + Element : false, + Elements : false, + Event : false, + Events : false, + Fx : false, + Group : false, + Hash : false, + HtmlTable : false, + Iframe : false, + IframeShim : false, + InputValidator: false, + instanceOf : false, + Keyboard : false, + Locale : false, + Mask : false, + MooTools : false, + Native : false, + Options : false, + OverText : false, + Request : false, + Scroller : false, + Slick : false, + Slider : false, + Sortables : false, + Spinner : false, + Swiff : false, + Tips : false, + Type : false, + typeOf : false, + URI : false, + Window : false +}; + +exports.prototypejs = { + "$" : false, + "$$" : false, + "$A" : false, + "$F" : false, + "$H" : false, + "$R" : false, + "$break" : false, + "$continue" : false, + "$w" : false, + Abstract : false, + Ajax : false, + Class : false, + Enumerable : false, + Element : false, + Event : false, + Field : false, + Form : false, + Hash : false, + Insertion : false, + ObjectRange : false, + PeriodicalExecuter: false, + Position : false, + Prototype : false, + Selector : false, + Template : false, + Toggle : false, + Try : false, + Autocompleter : false, + Builder : false, + Control : false, + Draggable : false, + Draggables : false, + Droppables : false, + Effect : false, + Sortable : false, + SortableObserver : false, + Sound : false, + Scriptaculous : false +}; + +exports.yui = { + YUI : false, + Y : false, + YUI_config: false +}; + + +})() +}, +{}], +4:[function(req,module,exports){ + +"use string"; +exports.unsafeString = + /@cc|<\/?|script|\]\s*\]|<\s*!|</i; +exports.unsafeChars = + /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; +exports.needEsc = + /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; + +exports.needEscGlobal = + /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; +exports.starSlash = /\*\//; +exports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; +exports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; +exports.fallsThrough = /^\s*\/\*\s*falls?\sthrough\s*\*\/\s*$/; + +}, +{}], +5:[function(req,module,exports){ + + +var state = { + syntax: {}, + + reset: function () { + this.tokens = { + prev: null, + next: null, + curr: null + }; + + this.option = {}; + this.ignored = {}; + this.directive = {}; + this.jsonMode = false; + this.jsonWarnings = []; + this.lines = []; + this.tab = ""; + this.cache = {}; // Node.JS doesn't have Map. Sniff. + } +}; + +exports.state = state; + +}, +{}], +6:[function(req,module,exports){ +(function(){ + +exports.register = function (linter) { + + linter.on("Identifier", function style_scanProto(data) { + if (linter.getOption("proto")) { + return; + } + + if (data.name === "__proto__") { + linter.warn("W103", { + line: data.line, + char: data.char, + data: [ data.name ] + }); + } + }); + + linter.on("Identifier", function style_scanIterator(data) { + if (linter.getOption("iterator")) { + return; + } + + if (data.name === "__iterator__") { + linter.warn("W104", { + line: data.line, + char: data.char, + data: [ data.name ] + }); + } + }); + + linter.on("Identifier", function style_scanDangling(data) { + if (!linter.getOption("nomen")) { + return; + } + if (data.name === "_") { + return; + } + if (linter.getOption("node")) { + if (/^(__dirname|__filename)$/.test(data.name) && !data.isProperty) { + return; + } + } + + if (/^(_+.*|.*_+)$/.test(data.name)) { + linter.warn("W105", { + line: data.line, + char: data.from, + data: [ "dangling '_'", data.name ] + }); + } + }); + + linter.on("Identifier", function style_scanCamelCase(data) { + if (!linter.getOption("camelcase")) { + return; + } + + if (data.name.replace(/^_+/, "").indexOf("_") > -1 && !data.name.match(/^[A-Z0-9_]*$/)) { + linter.warn("W106", { + line: data.line, + char: data.from, + data: [ data.name ] + }); + } + }); + + linter.on("String", function style_scanQuotes(data) { + var quotmark = linter.getOption("quotmark"); + var code; + + if (!quotmark) { + return; + } + + if (quotmark === "single" && data.quote !== "'") { + code = "W109"; + } + + if (quotmark === "double" && data.quote !== "\"") { + code = "W108"; + } + + if (quotmark === true) { + if (!linter.getCache("quotmark")) { + linter.setCache("quotmark", data.quote); + } + + if (linter.getCache("quotmark") !== data.quote) { + code = "W110"; + } + } + + if (code) { + linter.warn(code, { + line: data.line, + char: data.char, + }); + } + }); + + linter.on("Number", function style_scanNumbers(data) { + if (data.value.charAt(0) === ".") { + linter.warn("W008", { + line: data.line, + char: data.char, + data: [ data.value ] + }); + } + + if (data.value.substr(data.value.length - 1) === ".") { + linter.warn("W047", { + line: data.line, + char: data.char, + data: [ data.value ] + }); + } + + if (/^00+/.test(data.value)) { + linter.warn("W046", { + line: data.line, + char: data.char, + data: [ data.value ] + }); + } + }); + + linter.on("String", function style_scanJavaScriptURLs(data) { + var re = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; + + if (linter.getOption("scripturl")) { + return; + } + + if (re.test(data.value)) { + linter.warn("W107", { + line: data.line, + char: data.char + }); + } + }); +}; +})() +}, +{}], +7:[function(req,module,exports){ +(function(global){/*global window, global*/ +var functions = [ + [log, "log"] + , [info, "info"] + , [warn, "warn"] + , [error, "error"] + , [time, "time"] + , [timeEnd, "timeEnd"] + , [trace, "trace"] + , [dir, "dir"] + , [assert, "assert"] +] + +for (var i = 0; i < functions.length; i++) { + var tuple = functions[i] + var f = tuple[0] + var name = tuple[1] + + if (!console[name]) { + console[name] = f + } +} + +module.exports = console + +function log() {} + +function info() {} + +function warn() {} + +function error() {} + +function time(label) {} + +function timeEnd(label) {} + +function trace() {} + +function dir(object) {} + +function assert(expression) {} + +})(window) +}, +{}], +"jshint":[function(req,module,exports){ +module.exports=req('E/GbHF'); +}, +{}],"E/GbHF":[function(req,module,exports){ +(function(){/*! + * JSHint, by JSHint Community. + * + * This file (and this file only) is licensed under the same slightly modified + * MIT license that JSLint is. It stops evil-doers everywhere: + * + * Copyright (c) 2002 Douglas Crockford (www.JSLint.com) + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * The Software shall be used for Good, not Evil. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +var _ = req("underscore"); +var events = req("events"); +var vars = req("../shared/vars.js"); +var messages = req("../shared/messages.js"); +var Lexer = req("./lex.js").Lexer; +var reg = req("./reg.js"); +var state = req("./state.js").state; +var style = req("./style.js"); +var console = req("console-browserify"); var JSHINT = (function () { - - - var anonname, // The guessed name for anonymous functions. + + var anonname, // The guessed name for anonymous functions. + api, // Extension API bang = { "<" : true, "<=" : true, @@ -2134,627 +3205,189 @@ var JSHINT = (function () { "%" : true }, boolOptions = { - asi : true, // if automatic semicolon insertion should be tolerated - bitwise : true, // if bitwise operators should not be allowed - boss : true, // if advanced usage of assignments should be allowed - browser : true, // if the standard browser globals should be predefined - camelcase : true, // if identifiers should be required in camel case - couch : true, // if CouchDB globals should be predefined - curly : true, // if curly braces around all blocks should be required - debug : true, // if debugger statements should be allowed - devel : true, // if logging globals should be predefined (console, - dojo : true, // if Dojo Toolkit globals should be predefined - eqeqeq : true, // if === should be required - eqnull : true, // if == null comparisons should be tolerated - es5 : true, // if ES5 syntax should be allowed - esnext : true, // if es.next specific syntax should be allowed - evil : true, // if eval should be allowed - expr : true, // if ExpressionStatement should be allowed as Programs - forin : true, // if for in statements must filter - funcscope : true, // if only function scope should be used for scope tests - globalstrict: true, // if global should be allowed (also - immed : true, // if immediate invocations must be wrapped in parens - iterator : true, // if the `__iterator__` property should be allowed - jquery : true, // if jQuery globals should be predefined - lastsemic : true, // if semicolons may be ommitted for the trailing - latedef : true, // if the use before definition should not be tolerated - laxbreak : true, // if line breaks should not be checked - laxcomma : true, // if line breaks should not be checked around commas - loopfunc : true, // if functions should be allowed to be defined within - mootools : true, // if MooTools globals should be predefined - multistr : true, // allow multiline strings - newcap : true, // if constructor names must be capitalized - noarg : true, // if arguments.caller and arguments.callee should be - node : true, // if the Node.js environment globals should be - noempty : true, // if empty blocks should be disallowed - nonew : true, // if using `new` for side-effects should be disallowed + asi : true, // if automatic semicolon insertion should be tolerated + bitwise : true, // if bitwise operators should not be allowed + boss : true, // if advanced usage of assignments should be allowed + browser : true, // if the standard browser globals should be predefined + camelcase : true, // if identifiers should be required in camel case + couch : true, // if CouchDB globals should be predefined + curly : true, // if curly braces around all blocks should be required + debug : true, // if debugger statements should be allowed + devel : true, // if logging globals should be predefined (console, alert, etc.) + dojo : true, // if Dojo Toolkit globals should be predefined + eqeqeq : true, // if === should be required + eqnull : true, // if == null comparisons should be tolerated + es3 : true, // if ES3 syntax should be allowed + es5 : true, // if ES5 syntax should be allowed (is now set per default) + esnext : true, // if es.next specific syntax should be allowed + moz : true, // if mozilla specific syntax should be allowed + evil : true, // if eval should be allowed + expr : true, // if ExpressionStatement should be allowed as Programs + forin : true, // if for in statements must filter + funcscope : true, // if only function scope should be used for scope tests + gcl : true, // if JSHint should be compatible with Google Closure Linter + globalstrict: true, // if global should be allowed (also enables 'strict') + immed : true, // if immediate invocations must be wrapped in parens + iterator : true, // if the `__iterator__` property should be allowed + jquery : true, // if jQuery globals should be predefined + lastsemic : true, // if semicolons may be ommitted for the trailing + laxbreak : true, // if line breaks should not be checked + laxcomma : true, // if line breaks should not be checked around commas + loopfunc : true, // if functions should be allowed to be defined within + mootools : true, // if MooTools globals should be predefined + multistr : true, // allow multiline strings + newcap : true, // if constructor names must be capitalized + noarg : true, // if arguments.caller and arguments.callee should be + node : true, // if the Node.js environment globals should be + noempty : true, // if empty blocks should be disallowed + nonew : true, // if using `new` for side-effects should be disallowed nonstandard : true, // if non-standard (but widely adopted) globals should - nomen : true, // if names should be checked - onevar : true, // if only one var statement per function should be - onecase : true, // if one case switch statements should be allowed - passfail : true, // if the scan should stop on first error - plusplus : true, // if increment/decrement should not be allowed - proto : true, // if the `__proto__` property should be allowed + nomen : true, // if names should be checked + onevar : true, // if only one var statement per function should be + passfail : true, // if the scan should stop on first error + phantom : true, // if PhantomJS symbols should be allowed + plusplus : true, // if increment/decrement should not be allowed + proto : true, // if the `__proto__` property should be allowed prototypejs : true, // if Prototype and Scriptaculous globals should be - regexdash : true, // if unescaped first/last dash (-) inside brackets - regexp : true, // if the . should not be allowed in regexp literals - rhino : true, // if the Rhino environment globals should be predefined - undef : true, // if variables should be declared before used - unused : true, // if variables should be always used - scripturl : true, // if script-targeted URLs should be tolerated - shadow : true, // if variable shadowing should be tolerated - smarttabs : true, // if smarttabs should be tolerated - strict : true, // require the pragma - sub : true, // if all forms of subscript notation are tolerated - supernew : true, // if `new function () { ... };` and `new Object;` - trailing : true, // if trailing whitespace rules apply - validthis : true, // if 'this' inside a non-constructor function is valid. - withstmt : true, // if with statements should be allowed - white : true, // if strict whitespace rules apply - worker : true, // if Web Worker script symbols should be allowed - wsh : true, // if the Windows Scripting Host environment globals - yui : true // YUI variables should be predefined + rhino : true, // if the Rhino environment globals should be predefined + shelljs : true, // if ShellJS globals should be predefined + undef : true, // if variables should be declared before used + scripturl : true, // if script-targeted URLs should be tolerated + shadow : true, // if variable shadowing should be tolerated + smarttabs : true, // if smarttabs should be tolerated + strict : true, // require the pragma + sub : true, // if all forms of subscript notation are tolerated + supernew : true, // if `new function () { ... };` and `new Object;` + trailing : true, // if trailing whitespace rules apply + validthis : true, // if 'this' inside a non-constructor function is valid. + withstmt : true, // if with statements should be allowed + white : true, // if strict whitespace rules apply + worker : true, // if Web Worker script symbols should be allowed + wsh : true, // if the Windows Scripting Host environment globals + yui : true, // YUI variables should be predefined + onecase : true, // if one case switch statements should be allowed + regexp : true, // if the . should not be allowed in regexp literals + regexdash : true // if unescaped first/last dash (-) inside brackets }, valOptions = { - maxlen : false, - indent : false, - maxerr : false, - predef : false, - quotmark : false, //'single'|'double'|true - scope : false, + maxlen : false, + indent : false, + maxerr : false, + predef : false, + quotmark : false, //'single'|'double'|true + scope : false, maxstatements: false, // {int} max statements per function - maxdepth : false, // {int} max nested block depth per function - maxparams : false, // {int} max params per function - maxcomplexity: false // {int} max cyclomatic complexity per function + maxdepth : false, // {int} max nested block depth per function + maxparams : false, // {int} max params per function + maxcomplexity: false, // {int} max cyclomatic complexity per function + unused : true, // warn if variables are unused. Available options: + latedef : false // warn if the variable is used before its definition }, invertedOptions = { - bitwise : true, - forin : true, - newcap : true, - nomen : true, - plusplus : true, - regexp : true, - undef : true, - white : true, - eqeqeq : true, - onevar : true + bitwise : true, + forin : true, + newcap : true, + nomen : true, + plusplus: true, + regexp : true, + undef : true, + white : true, + eqeqeq : true, + onevar : true, + strict : true }, renamedOptions = { - eqeq : "eqeqeq", - vars : "onevar", - windows : "wsh" - }, - browser = { - ArrayBuffer : false, - ArrayBufferView : false, - Audio : false, - Blob : false, - addEventListener : false, - applicationCache : false, - atob : false, - blur : false, - btoa : false, - clearInterval : false, - clearTimeout : false, - close : false, - closed : false, - DataView : false, - DOMParser : false, - defaultStatus : false, - document : false, - event : false, - FileReader : false, - Float32Array : false, - Float64Array : false, - FormData : false, - focus : false, - frames : false, - getComputedStyle : false, - HTMLElement : false, - HTMLAnchorElement : false, - HTMLBaseElement : false, - HTMLBlockquoteElement : false, - HTMLBodyElement : false, - HTMLBRElement : false, - HTMLButtonElement : false, - HTMLCanvasElement : false, - HTMLDirectoryElement : false, - HTMLDivElement : false, - HTMLDListElement : false, - HTMLFieldSetElement : false, - HTMLFontElement : false, - HTMLFormElement : false, - HTMLFrameElement : false, - HTMLFrameSetElement : false, - HTMLHeadElement : false, - HTMLHeadingElement : false, - HTMLHRElement : false, - HTMLHtmlElement : false, - HTMLIFrameElement : false, - HTMLImageElement : false, - HTMLInputElement : false, - HTMLIsIndexElement : false, - HTMLLabelElement : false, - HTMLLayerElement : false, - HTMLLegendElement : false, - HTMLLIElement : false, - HTMLLinkElement : false, - HTMLMapElement : false, - HTMLMenuElement : false, - HTMLMetaElement : false, - HTMLModElement : false, - HTMLObjectElement : false, - HTMLOListElement : false, - HTMLOptGroupElement : false, - HTMLOptionElement : false, - HTMLParagraphElement : false, - HTMLParamElement : false, - HTMLPreElement : false, - HTMLQuoteElement : false, - HTMLScriptElement : false, - HTMLSelectElement : false, - HTMLStyleElement : false, - HTMLTableCaptionElement : false, - HTMLTableCellElement : false, - HTMLTableColElement : false, - HTMLTableElement : false, - HTMLTableRowElement : false, - HTMLTableSectionElement : false, - HTMLTextAreaElement : false, - HTMLTitleElement : false, - HTMLUListElement : false, - HTMLVideoElement : false, - history : false, - Int16Array : false, - Int32Array : false, - Int8Array : false, - Image : false, - length : false, - localStorage : false, - location : false, - MessageChannel : false, - MessageEvent : false, - MessagePort : false, - moveBy : false, - moveTo : false, - MutationObserver : false, - name : false, - Node : false, - NodeFilter : false, - navigator : false, - onbeforeunload : true, - onblur : true, - onerror : true, - onfocus : true, - onload : true, - onresize : true, - onunload : true, - open : false, - openDatabase : false, - opener : false, - Option : false, - parent : false, - print : false, - removeEventListener : false, - resizeBy : false, - resizeTo : false, - screen : false, - scroll : false, - scrollBy : false, - scrollTo : false, - sessionStorage : false, - setInterval : false, - setTimeout : false, - SharedWorker : false, - status : false, - top : false, - Uint16Array : false, - Uint32Array : false, - Uint8Array : false, - WebSocket : false, - window : false, - Worker : false, - XMLHttpRequest : false, - XMLSerializer : false, - XPathEvaluator : false, - XPathException : false, - XPathExpression : false, - XPathNamespace : false, - XPathNSResolver : false, - XPathResult : false - }, - - couch = { - "require" : false, - respond : false, - getRow : false, - emit : false, - send : false, - start : false, - sum : false, - log : false, - exports : false, - module : false, - provides : false + eqeq : "eqeqeq", + vars : "onevar", + windows: "wsh", + sloppy : "strict" }, declared, // Globals that were declared using /*global ... */ syntax. - - devel = { - alert : false, - confirm : false, - console : false, - Debug : false, - opera : false, - prompt : false - }, - - dojo = { - dojo : false, - dijit : false, - dojox : false, - define : false, - "require" : false - }, - - funct, // The current function + exported, // Variables that are used outside of the current file. functionicity = [ "closure", "exception", "global", "label", "outer", "unused", "var" ], - functions, // All of the functions + funct, // The current function + functions, // All of the functions - global, // The global scope - implied, // Implied globals + global, // The global scope + implied, // Implied globals inblock, indent, - jsonmode, - - jquery = { - "$" : false, - jQuery : false - }, - - lines, lookahead, + lex, member, membersOnly, - - mootools = { - "$" : false, - "$$" : false, - Asset : false, - Browser : false, - Chain : false, - Class : false, - Color : false, - Cookie : false, - Core : false, - Document : false, - DomReady : false, - DOMEvent : false, - DOMReady : false, - Drag : false, - Element : false, - Elements : false, - Event : false, - Events : false, - Fx : false, - Group : false, - Hash : false, - HtmlTable : false, - Iframe : false, - IframeShim : false, - InputValidator : false, - instanceOf : false, - Keyboard : false, - Locale : false, - Mask : false, - MooTools : false, - Native : false, - Options : false, - OverText : false, - Request : false, - Scroller : false, - Slick : false, - Slider : false, - Sortables : false, - Spinner : false, - Swiff : false, - Tips : false, - Type : false, - typeOf : false, - URI : false, - Window : false - }, - - nexttoken, - - node = { - __filename : false, - __dirname : false, - Buffer : false, - console : false, - exports : true, // In Node it is ok to exports = module.exports = foo(); - GLOBAL : false, - global : false, - module : false, - process : false, - require : false, - setTimeout : false, - clearTimeout : false, - setInterval : false, - clearInterval : false - }, - noreach, - option, predefined, // Global variables defined by option - prereg, - prevtoken, - prototypejs = { - "$" : false, - "$$" : false, - "$A" : false, - "$F" : false, - "$H" : false, - "$R" : false, - "$break" : false, - "$continue" : false, - "$w" : false, - Abstract : false, - Ajax : false, - Class : false, - Enumerable : false, - Element : false, - Event : false, - Field : false, - Form : false, - Hash : false, - Insertion : false, - ObjectRange : false, - PeriodicalExecuter: false, - Position : false, - Prototype : false, - Selector : false, - Template : false, - Toggle : false, - Try : false, - Autocompleter : false, - Builder : false, - Control : false, - Draggable : false, - Draggables : false, - Droppables : false, - Effect : false, - Sortable : false, - SortableObserver : false, - Sound : false, - Scriptaculous : false - }, - - quotmark, - - rhino = { - defineClass : false, - deserialize : false, - gc : false, - help : false, - importPackage: false, - "java" : false, - load : false, - loadClass : false, - print : false, - quit : false, - readFile : false, - readUrl : false, - runCommand : false, - seal : false, - serialize : false, - spawn : false, - sync : false, - toint32 : false, - version : false - }, - - scope, // The current scope + scope, // The current scope stack, - standard = { - Array : false, - Boolean : false, - Date : false, - decodeURI : false, - decodeURIComponent : false, - encodeURI : false, - encodeURIComponent : false, - Error : false, - "eval" : false, - EvalError : false, - Function : false, - hasOwnProperty : false, - isFinite : false, - isNaN : false, - JSON : false, - Map : false, - Math : false, - NaN : false, - Number : false, - Object : false, - parseInt : false, - parseFloat : false, - RangeError : false, - ReferenceError : false, - RegExp : false, - Set : false, - String : false, - SyntaxError : false, - TypeError : false, - URIError : false, - WeakMap : false - }, - nonstandard = { - escape : false, - unescape : false - }, - - directive, - syntax = {}, - tab, - token, unuseds, urls, - useESNextSyntax, warnings, - worker = { - importScripts : true, - postMessage : true, - self : true - }, - - wsh = { - ActiveXObject : true, - Enumerator : true, - GetObject : true, - ScriptEngine : true, - ScriptEngineBuildVersion : true, - ScriptEngineMajorVersion : true, - ScriptEngineMinorVersion : true, - VBArray : true, - WSH : true, - WScript : true, - XDomainRequest : true - }, - - yui = { - YUI : false, - Y : false, - YUI_config : false - }; - var ax, cx, tx, nx, nxg, lx, ix, jx, ft; - (function () { - ax = /@cc|<\/?|script|\]\s*\]|<\s*!|</i; - cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; - tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/=(?!(\S*\/[gim]?))|\/(\*(jshint|jslint|members?|global)?|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/; - nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; - nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; - lx = /\*\//; - ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; - jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; - ft = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/; - }()); - - function F() {} // Used by Object.create - - function is_own(object, name) { - return Object.prototype.hasOwnProperty.call(object, name); - } + extraModules = [], + emitter = new events.EventEmitter(); function checkOption(name, t) { - if (valOptions[name] === undefined && boolOptions[name] === undefined) { - warning("Bad option: '" + name + "'.", t); + name = name.trim(); + + if (/^[+-]W\d{3}$/g.test(name)) { + return true; } + + if (valOptions[name] === undefined && boolOptions[name] === undefined) { + if (t.type !== "jslint") { + error("E001", t, name); + return false; + } + } + + return true; } function isString(obj) { return Object.prototype.toString.call(obj) === "[object String]"; } - if (typeof Array.isArray !== "function") { - Array.isArray = function (o) { - return Object.prototype.toString.apply(o) === "[object Array]"; - }; - } - - if (!Array.prototype.forEach) { - Array.prototype.forEach = function (fn, scope) { - var len = this.length; - - for (var i = 0; i < len; i++) { - fn.call(scope || this, this[i], i, this); - } - }; - } - - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { - if (this === null || this === undefined) { - throw new TypeError(); - } - - var t = new Object(this); - var len = t.length >>> 0; - - if (len === 0) { - return -1; - } - - var n = 0; - if (arguments.length > 0) { - n = Number(arguments[1]); - if (n != n) { // shortcut for verifying if it's NaN - n = 0; - } else if (n !== 0 && n != Infinity && n != -Infinity) { - n = (n > 0 || -1) * Math.floor(Math.abs(n)); - } - } - - if (n >= len) { - return -1; - } - - var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); - for (; k < len; k++) { - if (k in t && t[k] === searchElement) { - return k; - } - } - - return -1; - }; - } - - if (typeof Object.create !== "function") { - Object.create = function (o) { - F.prototype = o; - return new F(); - }; - } - - if (typeof Object.keys !== "function") { - Object.keys = function (o) { - var a = [], k; - for (k in o) { - if (is_own(o, k)) { - a.push(k); - } - } - return a; - }; - } - - function isAlpha(str) { - return (str >= "a" && str <= "z\uffff") || - (str >= "A" && str <= "Z\uffff"); - } - - function isDigit(str) { - return (str >= "0" && str <= "9"); - } - - function isIdentifier(token, value) { - if (!token) + function isIdentifier(tkn, value) { + if (!tkn) return false; - if (!token.identifier || token.value !== value) + if (!tkn.identifier || tkn.value !== value) return false; return true; } + function isReserved(token) { + if (!token.reserved) { + return false; + } + + if (token.meta && token.meta.isFutureReservedWord) { + if (state.option.inES5(true) && !token.meta.es5) { + return false; + } + if (token.meta.strictOnly) { + if (!state.option.strict && !state.directive["use strict"]) { + return false; + } + } + + if (token.isProperty) { + return false; + } + } + + return true; + } + function supplant(str, data) { return str.replace(/\{([^{}]*)\}/g, function (a, b) { var r = data[b]; @@ -2765,7 +3398,7 @@ var JSHINT = (function () { function combine(t, o) { var n; for (n in o) { - if (is_own(o, n) && !is_own(JSHINT.blacklist, n)) { + if (_.has(o, n) && !_.has(JSHINT.blacklist, n)) { t[n] = o[n]; } } @@ -2778,69 +3411,92 @@ var JSHINT = (function () { } function assume() { - if (option.couch) { - combine(predefined, couch); + if (state.option.couch) { + combine(predefined, vars.couch); } - if (option.rhino) { - combine(predefined, rhino); + if (state.option.rhino) { + combine(predefined, vars.rhino); } - if (option.prototypejs) { - combine(predefined, prototypejs); + if (state.option.shelljs) { + combine(predefined, vars.shelljs); } - if (option.node) { - combine(predefined, node); - option.globalstrict = true; + if (state.option.phantom) { + combine(predefined, vars.phantom); } - if (option.devel) { - combine(predefined, devel); + if (state.option.prototypejs) { + combine(predefined, vars.prototypejs); } - if (option.dojo) { - combine(predefined, dojo); + if (state.option.node) { + combine(predefined, vars.node); } - if (option.browser) { - combine(predefined, browser); + if (state.option.devel) { + combine(predefined, vars.devel); } - if (option.nonstandard) { - combine(predefined, nonstandard); + if (state.option.dojo) { + combine(predefined, vars.dojo); } - if (option.jquery) { - combine(predefined, jquery); + if (state.option.browser) { + combine(predefined, vars.browser); } - if (option.mootools) { - combine(predefined, mootools); + if (state.option.nonstandard) { + combine(predefined, vars.nonstandard); } - if (option.worker) { - combine(predefined, worker); + if (state.option.jquery) { + combine(predefined, vars.jquery); } - if (option.wsh) { - combine(predefined, wsh); + if (state.option.mootools) { + combine(predefined, vars.mootools); } - if (option.esnext) { - useESNextSyntax(); + if (state.option.worker) { + combine(predefined, vars.worker); } - if (option.globalstrict && option.strict !== false) { - option.strict = true; + if (state.option.wsh) { + combine(predefined, vars.wsh); } - if (option.yui) { - combine(predefined, yui); + if (state.option.globalstrict && state.option.strict !== false) { + state.option.strict = true; } + + if (state.option.yui) { + combine(predefined, vars.yui); + } + + state.option.inMoz = function (strict) { + return state.option.moz; + }; + + state.option.inESNext = function (strict) { + return state.option.moz || state.option.esnext; + }; + + state.option.inES5 = function (/* strict */) { + return !state.option.es3; + }; + + state.option.inES3 = function (strict) { + if (strict) { + return !state.option.moz && !state.option.esnext && state.option.es3; + } + return state.option.es3; + }; } - function quit(message, line, chr) { - var percentage = Math.floor((line / lines.length) * 100); + function quit(code, line, chr) { + var percentage = Math.floor((line / state.lines.length) * 100); + var message = messages.errors[code].desc; throw { name: "JSHintError", @@ -2851,22 +3507,37 @@ var JSHINT = (function () { }; } - function isundef(scope, m, t, a) { - return JSHINT.undefs.push([scope, m, t, a]); + function isundef(scope, code, token, a) { + return JSHINT.undefs.push([scope, code, token, a]); } - function warning(m, t, a, b, c, d) { - var ch, l, w; - t = t || nexttoken; - if (t.id === "(end)") { // `~ - t = token; + function warning(code, t, a, b, c, d) { + var ch, l, w, msg; + + if (/^W\d{3}$/.test(code)) { + if (state.ignored[code]) + return; + + msg = messages.warnings[code]; + } else if (/E\d{3}/.test(code)) { + msg = messages.errors[code]; + } else if (/I\d{3}/.test(code)) { + msg = messages.info[code]; } + + t = t || state.tokens.next; + if (t.id === "(end)") { // `~ + t = state.tokens.curr; + } + l = t.line || 0; ch = t.from || 0; + w = { id: "(error)", - raw: m, - evidence: lines[l - 1] || "", + raw: msg.desc, + code: msg.code, + evidence: state.lines[l - 1] || "", line: l, character: ch, scope: JSHINT.scope, @@ -2875,15 +3546,19 @@ var JSHINT = (function () { c: c, d: d }; - w.reason = supplant(m, w); + + w.reason = supplant(msg.desc, w); JSHINT.errors.push(w); - if (option.passfail) { - quit("Stopping. ", l, ch); + + if (state.option.passfail) { + quit("E042", l, ch); } + warnings += 1; - if (warnings >= option.maxerr) { - quit("Too many errors.", l, ch); + if (warnings >= state.option.maxerr) { + quit("E043", l, ch); } + return w; } @@ -2915,905 +3590,258 @@ var JSHINT = (function () { return i; } - var lex = (function lex() { - var character, from, line, s; - - function nextLine() { - var at, - match, - tw; // trailing whitespace check - - if (line >= lines.length) - return false; - - character = 1; - s = lines[line]; - line += 1; - if (option.smarttabs) { - match = s.match(/(\/\/)? \t/); - at = match && !match[1] ? 0 : -1; - } else { - at = s.search(/ \t|\t [^\*]/); - } - - if (at >= 0) - warningAt("Mixed spaces and tabs.", line, at + 1); - - s = s.replace(/\t/g, tab); - at = s.search(cx); - - if (at >= 0) - warningAt("Unsafe character.", line, at); - - if (option.maxlen && option.maxlen < s.length) - warningAt("Line too long.", line, s.length); - tw = option.trailing && s.match(/^(.*?)\s+$/); - if (tw && !/^\s+$/.test(s)) { - warningAt("Trailing whitespace.", line, tw[1].length + 1); - } - return true; - } - - function it(type, value) { - var i, t; - - function checkName(name) { - if (!option.proto && name === "__proto__") { - warningAt("The '{a}' property is deprecated.", line, from, name); - return; - } - - if (!option.iterator && name === "__iterator__") { - warningAt("'{a}' is only available in JavaScript 1.7.", line, from, name); - return; - } - - var hasDangling = /^(_+.*|.*_+)$/.test(name); - - if (option.nomen && hasDangling && name !== "_") { - if (option.node && token.id !== "." && /^(__dirname|__filename)$/.test(name)) - return; - - warningAt("Unexpected {a} in '{b}'.", line, from, "dangling '_'", name); - return; - } - - if (option.camelcase) { - if (name.replace(/^_+/, "").indexOf("_") > -1 && !name.match(/^[A-Z0-9_]*$/)) { - warningAt("Identifier '{a}' is not in camel case.", line, from, value); - } - } - } - - if (type === "(color)" || type === "(range)") { - t = {type: type}; - } else if (type === "(punctuator)" || - (type === "(identifier)" && is_own(syntax, value))) { - t = syntax[value] || syntax["(error)"]; - } else { - t = syntax[type]; - } - - t = Object.create(t); - - if (type === "(string)" || type === "(range)") { - if (!option.scripturl && jx.test(value)) { - warningAt("Script URL.", line, from); - } - } - - if (type === "(identifier)") { - t.identifier = true; - checkName(value); - } - - t.value = value; - t.line = line; - t.character = character; - t.from = from; - i = t.id; - if (i !== "(endline)") { - prereg = i && - (("(,=:[!&|?{};".indexOf(i.charAt(i.length - 1)) >= 0) || - i === "return" || - i === "case"); - } - return t; - } - return { - init: function (source) { - if (typeof source === "string") { - lines = source - .replace(/\r\n/g, "\n") - .replace(/\r/g, "\n") - .split("\n"); - } else { - lines = source; - } - if (lines[0] && lines[0].substr(0, 2) === "#!") - lines[0] = ""; - - line = 0; - nextLine(); - from = 1; - }, - - range: function (begin, end) { - var c, value = ""; - from = character; - if (s.charAt(0) !== begin) { - errorAt("Expected '{a}' and instead saw '{b}'.", - line, character, begin, s.charAt(0)); - } - for (;;) { - s = s.slice(1); - character += 1; - c = s.charAt(0); - switch (c) { - case "": - errorAt("Missing '{a}'.", line, character, c); - break; - case end: - s = s.slice(1); - character += 1; - return it("(range)", value); - case "\\": - warningAt("Unexpected '{a}'.", line, character, c); - } - value += c; - } - - }, - token: function () { - var b, c, captures, d, depth, high, i, l, low, q, t, isLiteral, isInRange, n; - - function match(x) { - var r = x.exec(s), r1; - - if (r) { - l = r[0].length; - r1 = r[1]; - c = r1.charAt(0); - s = s.substr(l); - from = character + l - r1.length; - character += l; - return r1; - } - } - - function string(x) { - var c, j, r = "", allowNewLine = false; - - if (jsonmode && x !== "\"") { - warningAt("Strings must use doublequote.", - line, character); - } - - if (option.quotmark) { - if (option.quotmark === "single" && x !== "'") { - warningAt("Strings must use singlequote.", - line, character); - } else if (option.quotmark === "double" && x !== "\"") { - warningAt("Strings must use doublequote.", - line, character); - } else if (option.quotmark === true) { - quotmark = quotmark || x; - if (quotmark !== x) { - warningAt("Mixed double and single quotes.", - line, character); - } - } - } - - function esc(n) { - var i = parseInt(s.substr(j + 1, n), 16); - j += n; - if (i >= 32 && i <= 126 && - i !== 34 && i !== 92 && i !== 39) { - warningAt("Unnecessary escapement.", line, character); - } - character += n; - c = String.fromCharCode(i); - } - - j = 0; - -unclosedString: - for (;;) { - while (j >= s.length) { - j = 0; - - var cl = line, cf = from; - if (!nextLine()) { - errorAt("Unclosed string.", cl, cf); - break unclosedString; - } - - if (allowNewLine) { - allowNewLine = false; - } else { - warningAt("Unclosed string.", cl, cf); - } - } - - c = s.charAt(j); - if (c === x) { - character += 1; - s = s.substr(j + 1); - return it("(string)", r, x); - } - - if (c < " ") { - if (c === "\n" || c === "\r") { - break; - } - warningAt("Control character in string: {a}.", - line, character + j, s.slice(0, j)); - } else if (c === "\\") { - j += 1; - character += 1; - c = s.charAt(j); - n = s.charAt(j + 1); - switch (c) { - case "\\": - case "\"": - case "/": - break; - case "\'": - if (jsonmode) { - warningAt("Avoid \\'.", line, character); - } - break; - case "b": - c = "\b"; - break; - case "f": - c = "\f"; - break; - case "n": - c = "\n"; - break; - case "r": - c = "\r"; - break; - case "t": - c = "\t"; - break; - case "0": - c = "\0"; - if (n >= 0 && n <= 7 && directive["use strict"]) { - warningAt( - "Octal literals are not allowed in strict mode.", - line, character); - } - break; - case "u": - esc(4); - break; - case "v": - if (jsonmode) { - warningAt("Avoid \\v.", line, character); - } - c = "\v"; - break; - case "x": - if (jsonmode) { - warningAt("Avoid \\x-.", line, character); - } - esc(2); - break; - case "": - allowNewLine = true; - if (option.multistr) { - if (jsonmode) { - warningAt("Avoid EOL escapement.", line, character); - } - c = ""; - character -= 1; - break; - } - warningAt("Bad escapement of EOL. Use option multistr if needed.", - line, character); - break; - case "!": - if (s.charAt(j - 2) === "<") - break; - default: - warningAt("Bad escapement.", line, character); - } - } - r += c; - character += 1; - j += 1; - } - } - - for (;;) { - if (!s) { - return it(nextLine() ? "(endline)" : "(end)", ""); - } - - t = match(tx); - - if (!t) { - t = ""; - c = ""; - while (s && s < "!") { - s = s.substr(1); - } - if (s) { - errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1)); - s = ""; - } - } else { - - if (isAlpha(c) || c === "_" || c === "$") { - return it("(identifier)", t); - } - - if (isDigit(c)) { - if (!isFinite(Number(t))) { - warningAt("Bad number '{a}'.", - line, character, t); - } - if (isAlpha(s.substr(0, 1))) { - warningAt("Missing space after '{a}'.", - line, character, t); - } - if (c === "0") { - d = t.substr(1, 1); - if (isDigit(d)) { - if (token.id !== ".") { - warningAt("Don't use extra leading zeros '{a}'.", - line, character, t); - } - } else if (jsonmode && (d === "x" || d === "X")) { - warningAt("Avoid 0x-. '{a}'.", - line, character, t); - } - } - if (t.substr(t.length - 1) === ".") { - warningAt( -"A trailing decimal point can be confused with a dot '{a}'.", line, character, t); - } - return it("(number)", t); - } - switch (t) { - - case "\"": - case "'": - return string(t); - - case "//": - s = ""; - token.comment = true; - break; - - case "/*": - for (;;) { - i = s.search(lx); - if (i >= 0) { - break; - } - if (!nextLine()) { - errorAt("Unclosed comment.", line, character); - } - } - s = s.substr(i + 2); - token.comment = true; - break; - - case "/*members": - case "/*member": - case "/*jshint": - case "/*jslint": - case "/*global": - case "*/": - return { - value: t, - type: "special", - line: line, - character: character, - from: from - }; - - case "": - break; - case "/": - if (s.charAt(0) === "=") { - errorAt("A regular expression literal can be confused with '/='.", - line, from); - } - - if (prereg) { - depth = 0; - captures = 0; - l = 0; - for (;;) { - b = true; - c = s.charAt(l); - l += 1; - switch (c) { - case "": - errorAt("Unclosed regular expression.", line, from); - return quit("Stopping.", line, from); - case "/": - if (depth > 0) { - warningAt("{a} unterminated regular expression " + - "group(s).", line, from + l, depth); - } - c = s.substr(0, l - 1); - q = { - g: true, - i: true, - m: true - }; - while (q[s.charAt(l)] === true) { - q[s.charAt(l)] = false; - l += 1; - } - character += l; - s = s.substr(l); - q = s.charAt(0); - if (q === "/" || q === "*") { - errorAt("Confusing regular expression.", - line, from); - } - return it("(regexp)", c); - case "\\": - c = s.charAt(l); - if (c < " ") { - warningAt( -"Unexpected control character in regular expression.", line, from + l); - } else if (c === "<") { - warningAt( -"Unexpected escaped character '{a}' in regular expression.", line, from + l, c); - } - l += 1; - break; - case "(": - depth += 1; - b = false; - if (s.charAt(l) === "?") { - l += 1; - switch (s.charAt(l)) { - case ":": - case "=": - case "!": - l += 1; - break; - default: - warningAt( -"Expected '{a}' and instead saw '{b}'.", line, from + l, ":", s.charAt(l)); - } - } else { - captures += 1; - } - break; - case "|": - b = false; - break; - case ")": - if (depth === 0) { - warningAt("Unescaped '{a}'.", - line, from + l, ")"); - } else { - depth -= 1; - } - break; - case " ": - q = 1; - while (s.charAt(l) === " ") { - l += 1; - q += 1; - } - if (q > 1) { - warningAt( -"Spaces are hard to count. Use {{a}}.", line, from + l, q); - } - break; - case "[": - c = s.charAt(l); - if (c === "^") { - l += 1; - if (s.charAt(l) === "]") { - errorAt("Unescaped '{a}'.", - line, from + l, "^"); - } - } - if (c === "]") { - warningAt("Empty class.", line, - from + l - 1); - } - isLiteral = false; - isInRange = false; -klass: - do { - c = s.charAt(l); - l += 1; - switch (c) { - case "[": - case "^": - warningAt("Unescaped '{a}'.", - line, from + l, c); - if (isInRange) { - isInRange = false; - } else { - isLiteral = true; - } - break; - case "-": - if (isLiteral && !isInRange) { - isLiteral = false; - isInRange = true; - } else if (isInRange) { - isInRange = false; - } else if (s.charAt(l) === "]") { - isInRange = true; - } else { - if (option.regexdash !== (l === 2 || (l === 3 && - s.charAt(1) === "^"))) { - warningAt("Unescaped '{a}'.", - line, from + l - 1, "-"); - } - isLiteral = true; - } - break; - case "]": - if (isInRange && !option.regexdash) { - warningAt("Unescaped '{a}'.", - line, from + l - 1, "-"); - } - break klass; - case "\\": - c = s.charAt(l); - if (c < " ") { - warningAt( -"Unexpected control character in regular expression.", line, from + l); - } else if (c === "<") { - warningAt( -"Unexpected escaped character '{a}' in regular expression.", line, from + l, c); - } - l += 1; - if (/[wsd]/i.test(c)) { - if (isInRange) { - warningAt("Unescaped '{a}'.", - line, from + l, "-"); - isInRange = false; - } - isLiteral = false; - } else if (isInRange) { - isInRange = false; - } else { - isLiteral = true; - } - break; - case "/": - warningAt("Unescaped '{a}'.", - line, from + l - 1, "/"); - - if (isInRange) { - isInRange = false; - } else { - isLiteral = true; - } - break; - case "<": - if (isInRange) { - isInRange = false; - } else { - isLiteral = true; - } - break; - default: - if (isInRange) { - isInRange = false; - } else { - isLiteral = true; - } - } - } while (c); - break; - case ".": - if (option.regexp) { - warningAt("Insecure '{a}'.", line, - from + l, c); - } - break; - case "]": - case "?": - case "{": - case "}": - case "+": - case "*": - warningAt("Unescaped '{a}'.", line, - from + l, c); - } - if (b) { - switch (s.charAt(l)) { - case "?": - case "+": - case "*": - l += 1; - if (s.charAt(l) === "?") { - l += 1; - } - break; - case "{": - l += 1; - c = s.charAt(l); - if (c < "0" || c > "9") { - warningAt( -"Expected a number and instead saw '{a}'.", line, from + l, c); - break; // No reason to continue checking numbers. - } - l += 1; - low = +c; - for (;;) { - c = s.charAt(l); - if (c < "0" || c > "9") { - break; - } - l += 1; - low = +c + (low * 10); - } - high = low; - if (c === ",") { - l += 1; - high = Infinity; - c = s.charAt(l); - if (c >= "0" && c <= "9") { - l += 1; - high = +c; - for (;;) { - c = s.charAt(l); - if (c < "0" || c > "9") { - break; - } - l += 1; - high = +c + (high * 10); - } - } - } - if (s.charAt(l) !== "}") { - warningAt( -"Expected '{a}' and instead saw '{b}'.", line, from + l, "}", c); - } else { - l += 1; - } - if (s.charAt(l) === "?") { - l += 1; - } - if (low > high) { - warningAt( -"'{a}' should not be greater than '{b}'.", line, from + l, low, high); - } - } - } - } - c = s.substr(0, l - 1); - character += l; - s = s.substr(l); - return it("(regexp)", c); - } - return it("(punctuator)", t); - - case "#": - return it("(punctuator)", t); - default: - return it("(punctuator)", t); - } - } - } - } - }; - }()); - - - function addlabel(t, type, token) { - if (t === "hasOwnProperty") { - warning("'hasOwnProperty' is a really bad name."); - } + function addlabel(t, type, tkn, islet) { if (type === "exception") { - if (is_own(funct["(context)"], t)) { - if (funct[t] !== true && !option.node) { - warning("Value of '{a}' may be overwritten in IE.", nexttoken, t); + if (_.has(funct["(context)"], t)) { + if (funct[t] !== true && !state.option.node) { + warning("W002", state.tokens.next, t); } } } - if (is_own(funct, t) && !funct["(global)"]) { + if (_.has(funct, t) && !funct["(global)"]) { if (funct[t] === true) { - if (option.latedef) - warning("'{a}' was used before it was defined.", nexttoken, t); + if (state.option.latedef) { + if ((state.option.latedef === true && _.contains([funct[t], type], "unction")) || + !_.contains([funct[t], type], "unction")) { + warning("W003", state.tokens.next, t); + } + } } else { - if (!option.shadow && type !== "exception") { - warning("'{a}' is already defined.", nexttoken, t); + if (!state.option.shadow && type !== "exception" || + (funct["(blockscope)"].getlabel(t))) { + warning("W004", state.tokens.next, t); } } } - - funct[t] = type; - - if (token) { - funct["(tokens)"][t] = token; + if (funct["(blockscope)"] && funct["(blockscope)"].current.has(t)) { + error("E044", state.tokens.next, t); } - - if (funct["(global)"]) { - global[t] = funct; - if (is_own(implied, t)) { - if (option.latedef) - warning("'{a}' was used before it was defined.", nexttoken, t); - delete implied[t]; - } + if (islet) { + funct["(blockscope)"].current.add(t, type, state.tokens.curr); } else { - scope[t] = funct; + + funct[t] = type; + + if (tkn) { + funct["(tokens)"][t] = tkn; + } + + if (funct["(global)"]) { + global[t] = funct; + if (_.has(implied, t)) { + if (state.option.latedef) { + if ((state.option.latedef === true && _.contains([funct[t], type], "unction")) || + !_.contains([funct[t], type], "unction")) { + warning("W003", state.tokens.next, t); + } + } + + delete implied[t]; + } + } else { + scope[t] = funct; + } } } - function doOption() { - var nt = nexttoken; - var o = nt.value; - var quotmarkValue = option.quotmark; + var nt = state.tokens.next; + var body = nt.body.split(",").map(function (s) { return s.trim(); }); var predef = {}; - var b, obj, filter, t, tn, v, minus; - switch (o) { - case "*/": - error("Unbegun comment."); - break; - case "/*members": - case "/*member": - o = "/*members"; - if (!membersOnly) { - membersOnly = {}; + if (nt.type === "globals") { + body.forEach(function (g) { + g = g.split(":"); + var key = g[0]; + var val = g[1]; + + if (key.charAt(0) === "-") { + key = key.slice(1); + val = false; + + JSHINT.blacklist[key] = key; + updatePredefined(); + } else { + predef[key] = (val === "true"); + } + }); + + combine(predefined, predef); + + for (var key in predef) { + if (_.has(predef, key)) { + declared[key] = nt; + } } - obj = membersOnly; - option.quotmark = false; - break; - case "/*jshint": - case "/*jslint": - obj = option; - filter = boolOptions; - break; - case "/*global": - obj = predef; - break; - default: - error("What?"); } - t = lex.token(); + if (nt.type === "exported") { + body.forEach(function (e) { + exported[e] = true; + }); + } - for (;;) { - minus = false; - var breakOuterLoop; - for (;;) { - if (t.type === "special" && t.value === "*/") { - breakOuterLoop = true; - break; - } - if (t.id !== "(endline)" && t.id !== ",") { - break; - } - t = lex.token(); - } - if (breakOuterLoop) - break; + if (nt.type === "members") { + membersOnly = membersOnly || {}; - if (o === "/*global" && t.value === "-") { - minus = true; - t = lex.token(); - } + body.forEach(function (m) { + var ch1 = m.charAt(0); + var ch2 = m.charAt(m.length - 1); - if (t.type !== "(string)" && t.type !== "(identifier)" && o !== "/*members") { - error("Bad option.", t); - } - - v = lex.token(); - if (v.id === ":") { - v = lex.token(); - - if (obj === membersOnly) { - error("Expected '{a}' and instead saw '{b}'.", t, "*/", ":"); + if (ch1 === ch2 && (ch1 === "\"" || ch1 === "'")) { + m = m + .substr(1, m.length - 2) + .replace("\\b", "\b") + .replace("\\t", "\t") + .replace("\\n", "\n") + .replace("\\v", "\v") + .replace("\\f", "\f") + .replace("\\r", "\r") + .replace("\\\\", "\\") + .replace("\\\"", "\""); } - if (o === "/*jshint") { - checkOption(t.value, t); + membersOnly[m] = false; + }); + } + + var numvals = [ + "maxstatements", + "maxparams", + "maxdepth", + "maxcomplexity", + "maxerr", + "maxlen", + "indent" + ]; + + if (nt.type === "jshint" || nt.type === "jslint") { + body.forEach(function (g) { + g = g.split(":"); + var key = (g[0] || "").trim(); + var val = (g[1] || "").trim(); + + if (!checkOption(key, nt)) { + return; } - var numericVals = [ - "maxstatements", - "maxparams", - "maxdepth", - "maxcomplexity", - "maxerr", - "maxlen", - "indent" - ]; + if (numvals.indexOf(key) >= 0) { + if (val !== "false") { + val = +val; - if (numericVals.indexOf(t.value) > -1 && (o === "/*jshint" || o === "/*jslint")) { - b = +v.value; + if (typeof val !== "number" || !isFinite(val) || val <= 0 || Math.floor(val) !== val) { + error("E032", nt, g[1].trim()); + return; + } - if (typeof b !== "number" || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { - error("Expected a small integer and instead saw '{a}'.", v, v.value); - } - - if (t.value === "indent") - obj.white = true; - - obj[t.value] = b; - } else if (t.value === "validthis") { - if (funct["(global)"]) { - error("Option 'validthis' can't be used in a global scope."); + if (key === "indent") { + state.option["(explicitIndent)"] = true; + } + state.option[key] = val; } else { - if (v.value === "true" || v.value === "false") - obj[t.value] = v.value === "true"; - else - error("Bad option value.", v); + if (key === "indent") { + state.option["(explicitIndent)"] = false; + } else { + state.option[key] = false; + } } - } else if (t.value === "quotmark" && (o === "/*jshint")) { - switch (v.value) { + + return; + } + + if (key === "validthis") { + if (funct["(global)"]) { + error("E009"); + } else { + if (val === "true" || val === "false") { + state.option.validthis = (val === "true"); + } else { + error("E002", nt); + } + } + return; + } + + if (key === "quotmark") { + switch (val) { case "true": - obj.quotmark = true; - break; case "false": - obj.quotmark = false; + state.option.quotmark = (val === "true"); break; case "double": case "single": - obj.quotmark = v.value; + state.option.quotmark = val; break; default: - error("Bad option value.", v); + error("E002", nt); } - } else if (v.value === "true" || v.value === "false") { - if (o === "/*jslint") { - tn = renamedOptions[t.value] || t.value; - obj[tn] = v.value === "true"; + return; + } + + if (key === "unused") { + switch (val) { + case "true": + state.option.unused = true; + break; + case "false": + state.option.unused = false; + break; + case "vars": + case "strict": + state.option.unused = val; + break; + default: + error("E002", nt); + } + return; + } + + if (key === "latedef") { + switch (val) { + case "true": + state.option.latedef = true; + break; + case "false": + state.option.latedef = false; + break; + case "nofunc": + state.option.latedef = "nofunc"; + break; + default: + error("E002", nt); + } + return; + } + + var match = /^([+-])(W\d{3})$/g.exec(key); + if (match) { + state.ignored[match[2]] = (match[1] === "-"); + return; + } + + var tn; + if (val === "true" || val === "false") { + if (nt.type === "jslint") { + tn = renamedOptions[key] || key; + state.option[tn] = (val === "true"); + if (invertedOptions[tn] !== undefined) { - obj[tn] = !obj[tn]; + state.option[tn] = !state.option[tn]; } } else { - obj[t.value] = v.value === "true"; + state.option[key] = (val === "true"); } - if (t.value === "newcap") - obj["(explicitNewcap)"] = true; - } else { - error("Bad option value.", v); - } - t = lex.token(); - } else { - if (o === "/*jshint" || o === "/*jslint") { - error("Missing option value.", t); + if (key === "newcap") { + state.option["(explicitNewcap)"] = true; + } + return; } - obj[t.value] = false; + error("E002", nt); + }); - if (o === "/*global" && minus === true) { - JSHINT.blacklist[t.value] = t.value; - updatePredefined(); - } - - t = v; - } - } - - if (o === "/*members") { - option.quotmark = quotmarkValue; - } - - combine(predefined, predef); - - for (var key in predef) { - if (is_own(predef, key)) { - declared[key] = nt; - } - } - - if (filter) { assume(); } } @@ -3832,54 +3860,61 @@ klass: } function advance(id, t) { - switch (token.id) { + switch (state.tokens.curr.id) { case "(number)": - if (nexttoken.id === ".") { - warning("A dot following a number can be confused with a decimal point.", token); + if (state.tokens.next.id === ".") { + warning("W005", state.tokens.curr); } break; case "-": - if (nexttoken.id === "-" || nexttoken.id === "--") { - warning("Confusing minusses."); + if (state.tokens.next.id === "-" || state.tokens.next.id === "--") { + warning("W006"); } break; case "+": - if (nexttoken.id === "+" || nexttoken.id === "++") { - warning("Confusing plusses."); + if (state.tokens.next.id === "+" || state.tokens.next.id === "++") { + warning("W007"); } break; } - if (token.type === "(string)" || token.identifier) { - anonname = token.value; + if (state.tokens.curr.type === "(string)" || state.tokens.curr.identifier) { + anonname = state.tokens.curr.value; } - if (id && nexttoken.id !== id) { + if (id && state.tokens.next.id !== id) { if (t) { - if (nexttoken.id === "(end)") { - warning("Unmatched '{a}'.", t, t.id); + if (state.tokens.next.id === "(end)") { + error("E019", t, t.id); } else { - warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", - nexttoken, id, t.id, t.line, nexttoken.value); + error("E020", state.tokens.next, id, t.id, t.line, state.tokens.next.value); } - } else if (nexttoken.type !== "(identifier)" || - nexttoken.value !== id) { - warning("Expected '{a}' and instead saw '{b}'.", - nexttoken, id, nexttoken.value); + } else if (state.tokens.next.type !== "(identifier)" || state.tokens.next.value !== id) { + warning("W116", state.tokens.next, id, state.tokens.next.value); } } - prevtoken = token; - token = nexttoken; + state.tokens.prev = state.tokens.curr; + state.tokens.curr = state.tokens.next; for (;;) { - nexttoken = lookahead.shift() || lex.token(); - if (nexttoken.id === "(end)" || nexttoken.id === "(error)") { + state.tokens.next = lookahead.shift() || lex.token(); + + if (!state.tokens.next) { // No more tokens left, give up + quit("E041", state.tokens.curr.line); + } + + if (state.tokens.next.id === "(end)" || state.tokens.next.id === "(error)") { return; } - if (nexttoken.type === "special") { + + if (state.tokens.next.check) { + state.tokens.next.check(); + } + + if (state.tokens.next.isSpecial) { doOption(); } else { - if (nexttoken.id !== "(endline)") { + if (state.tokens.next.id !== "(endline)") { break; } } @@ -3887,84 +3922,100 @@ klass: } function expression(rbp, initial) { - var left, isArray = false, isObject = false; + var left, isArray = false, isObject = false, isLetExpr = false; + if (!initial && state.tokens.next.value === "let" && peek(0).value === "(") { + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.next, "let expressions"); + } + isLetExpr = true; + funct["(blockscope)"].stack(); + advance("let"); + advance("("); + state.syntax["let"].fud.call(state.syntax["let"].fud, false); + advance(")"); + } - if (nexttoken.id === "(end)") - error("Unexpected early end of program.", token); + if (state.tokens.next.id === "(end)") + error("E006", state.tokens.curr); advance(); + if (initial) { anonname = "anonymous"; - funct["(verb)"] = token.value; + funct["(verb)"] = state.tokens.curr.value; } - if (initial === true && token.fud) { - left = token.fud(); + + if (initial === true && state.tokens.curr.fud) { + left = state.tokens.curr.fud(); } else { - if (token.nud) { - left = token.nud(); + if (state.tokens.curr.nud) { + left = state.tokens.curr.nud(); } else { - if (nexttoken.type === "(number)" && token.id === ".") { - warning("A leading decimal point can be confused with a dot: '.{a}'.", - token, nexttoken.value); - advance(); - return token; - } else { - error("Expected an identifier and instead saw '{a}'.", - token, token.id); - } + error("E030", state.tokens.curr, state.tokens.curr.id); } - while (rbp < nexttoken.lbp) { - isArray = token.value === "Array"; - isObject = token.value === "Object"; + + var end_of_expr = state.tokens.next.identifier && + !state.tokens.curr.led && + state.tokens.curr.line !== state.tokens.next.line; + while (rbp < state.tokens.next.lbp && !end_of_expr) { + isArray = state.tokens.curr.value === "Array"; + isObject = state.tokens.curr.value === "Object"; if (left && (left.value || (left.first && left.first.value))) { if (left.value !== "new" || (left.first && left.first.value && left.first.value === ".")) { isArray = false; - if (left.value !== token.value) { + if (left.value !== state.tokens.curr.value) { isObject = false; } } } advance(); - if (isArray && token.id === "(" && nexttoken.id === ")") - warning("Use the array literal notation [].", token); - if (isObject && token.id === "(" && nexttoken.id === ")") - warning("Use the object literal notation {}.", token); - if (token.led) { - left = token.led(left); + + if (isArray && state.tokens.curr.id === "(" && state.tokens.next.id === ")") { + warning("W009", state.tokens.curr); + } + + if (isObject && state.tokens.curr.id === "(" && state.tokens.next.id === ")") { + warning("W010", state.tokens.curr); + } + + if (left && state.tokens.curr.led) { + left = state.tokens.curr.led(left); } else { - error("Expected an operator and instead saw '{a}'.", - token, token.id); + error("E033", state.tokens.curr, state.tokens.curr.id); } } } + if (isLetExpr) { + funct["(blockscope)"].unstack(); + } return left; } function adjacent(left, right) { - left = left || token; - right = right || nexttoken; - if (option.white) { + left = left || state.tokens.curr; + right = right || state.tokens.next; + if (state.option.white) { if (left.character !== right.from && left.line === right.line) { left.from += (left.character - left.from); - warning("Unexpected space after '{a}'.", left, left.value); + warning("W011", left, left.value); } } } function nobreak(left, right) { - left = left || token; - right = right || nexttoken; - if (option.white && (left.character !== right.from || left.line !== right.line)) { - warning("Unexpected space before '{a}'.", right, right.value); + left = left || state.tokens.curr; + right = right || state.tokens.next; + if (state.option.white && (left.character !== right.from || left.line !== right.line)) { + warning("W012", right, right.value); } } function nospace(left, right) { - left = left || token; - right = right || nexttoken; - if (option.white && !left.comment) { + left = left || state.tokens.curr; + right = right || state.tokens.next; + if (state.option.white && !left.comment) { if (left.line === right.line) { adjacent(left, right); } @@ -3972,77 +4023,132 @@ klass: } function nonadjacent(left, right) { - if (option.white) { - left = left || token; - right = right || nexttoken; + if (state.option.white) { + left = left || state.tokens.curr; + right = right || state.tokens.next; + if (left.value === ";" && right.value === ";") { return; } + if (left.line === right.line && left.character === right.from) { left.from += (left.character - left.from); - warning("Missing space after '{a}'.", - left, left.value); + warning("W013", left, left.value); } } } function nobreaknonadjacent(left, right) { - left = left || token; - right = right || nexttoken; - if (!option.laxbreak && left.line !== right.line) { - warning("Bad line breaking before '{a}'.", right, right.id); - } else if (option.white) { - left = left || token; - right = right || nexttoken; + left = left || state.tokens.curr; + right = right || state.tokens.next; + if (!state.option.laxbreak && left.line !== right.line) { + warning("W014", right, right.id); + } else if (state.option.white) { + left = left || state.tokens.curr; + right = right || state.tokens.next; if (left.character === right.from) { left.from += (left.character - left.from); - warning("Missing space after '{a}'.", - left, left.value); + warning("W013", left, left.value); } } } function indentation(bias) { - var i; - if (option.white && nexttoken.id !== "(end)") { - i = indent + (bias || 0); - if (nexttoken.from !== i) { - warning( -"Expected '{a}' to have an indentation at {b} instead at {c}.", - nexttoken, nexttoken.value, i, nexttoken.from); - } + if (!state.option.white && !state.option["(explicitIndent)"]) { + return; + } + + if (state.tokens.next.id === "(end)") { + return; + } + + var i = indent + (bias || 0); + if (state.tokens.next.from !== i) { + warning("W015", state.tokens.next, state.tokens.next.value, i, state.tokens.next.from); } } function nolinebreak(t) { - t = t || token; - if (t.line !== nexttoken.line) { - warning("Line breaking error '{a}'.", t, t.value); + t = t || state.tokens.curr; + if (t.line !== state.tokens.next.line) { + warning("E022", t, t.value); } } - function comma() { - if (token.line !== nexttoken.line) { - if (!option.laxcomma) { - if (comma.first) { - warning("Comma warnings can be turned off with 'laxcomma'"); - comma.first = false; + function comma(opts) { + opts = opts || {}; + + if (!opts.peek) { + if (state.tokens.curr.line !== state.tokens.next.line) { + if (!state.option.laxcomma) { + if (comma.first) { + warning("I001"); + comma.first = false; + } + warning("W014", state.tokens.curr, state.tokens.next.value); } - warning("Bad line breaking before '{a}'.", token, nexttoken.id); + } else if (!state.tokens.curr.comment && + state.tokens.curr.character !== state.tokens.next.from && state.option.white) { + state.tokens.curr.from += (state.tokens.curr.character - state.tokens.curr.from); + warning("W011", state.tokens.curr, state.tokens.curr.value); } - } else if (!token.comment && token.character !== nexttoken.from && option.white) { - token.from += (token.character - token.from); - warning("Unexpected space after '{a}'.", token, token.value); + + advance(","); } - advance(","); - nonadjacent(token, nexttoken); + + if (state.tokens.next.value !== "]" && state.tokens.next.value !== "}") { + nonadjacent(state.tokens.curr, state.tokens.next); + } + + if (state.tokens.next.identifier && !(opts.property && state.option.inES5())) { + switch (state.tokens.next.value) { + case "break": + case "case": + case "catch": + case "continue": + case "default": + case "do": + case "else": + case "finally": + case "for": + case "if": + case "in": + case "instanceof": + case "return": + case "yield": + case "switch": + case "throw": + case "try": + case "var": + case "let": + case "while": + case "with": + error("E024", state.tokens.next, state.tokens.next.value); + return false; + } + } + + if (state.tokens.next.type === "(punctuator)") { + switch (state.tokens.next.value) { + case "}": + case "]": + case ",": + if (opts.allowTrailing) { + return true; + } + case ")": + error("E024", state.tokens.next, state.tokens.next.value); + return false; + } + } + return true; } function symbol(s, p) { - var x = syntax[s]; + var x = state.syntax[s]; if (!x || typeof x !== "object") { - syntax[s] = x = { + state.syntax[s] = x = { id: s, lbp: p, value: s @@ -4051,12 +4157,10 @@ klass: return x; } - function delim(s) { return symbol(s, 0); } - function stmt(s, f) { var x = delim(s); x.identifier = x.reserved = true; @@ -4064,14 +4168,12 @@ klass: return x; } - function blockstmt(s, f) { var x = stmt(s, f); x.block = true; return x; } - function reserveName(x) { var c = x.id.charAt(0); if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) { @@ -4080,7 +4182,6 @@ klass: return x; } - function prefix(s, f) { var x = symbol(s, 150); reserveName(x); @@ -4088,11 +4189,11 @@ klass: this.right = expression(150); this.arity = "unary"; if (this.id === "++" || this.id === "--") { - if (option.plusplus) { - warning("Unexpected use of '{a}'.", this, this.id); - } else if ((!this.right.identifier || this.right.reserved) && + if (state.option.plusplus) { + warning("W016", this, this.id); + } else if ((!this.right.identifier || isReserved(this.right)) && this.right.id !== "." && this.right.id !== "[") { - warning("Bad operand.", this); + warning("W017", this); } } return this; @@ -4100,7 +4201,6 @@ klass: return x; } - function type(s, f) { var x = delim(s); x.type = s; @@ -4108,13 +4208,28 @@ klass: return x; } - - function reserve(s, f) { - var x = type(s, f); - x.identifier = x.reserved = true; + function reserve(name, func) { + var x = type(name, func); + x.identifier = true; + x.reserved = true; return x; } + function FutureReservedWord(name, meta) { + var x = type(name, (meta && meta.nud) || function () { + return this; + }); + + meta = meta || {}; + meta.isFutureReservedWord = true; + + x.value = name; + x.identifier = true; + x.reserved = true; + x.meta = meta; + + return x; + } function reservevar(s, v) { return reserve(s, function () { @@ -4125,17 +4240,16 @@ klass: }); } - function infix(s, f, p, w) { var x = symbol(s, p); reserveName(x); x.led = function (left) { if (!w) { - nobreaknonadjacent(prevtoken, token); - nonadjacent(token, nexttoken); + nobreaknonadjacent(state.tokens.prev, state.tokens.curr); + nonadjacent(state.tokens.curr, state.tokens.next); } if (s === "in" && left.id === "!") { - warning("Confusing use of '{a}'.", left, "!"); + warning("W018", left, "!"); } if (typeof f === "function") { return f(left, this); @@ -4149,24 +4263,50 @@ klass: } + function application(s) { + var x = symbol(s, 42); + + x.led = function (left) { + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "arrow function syntax (=>)"); + } + + nobreaknonadjacent(state.tokens.prev, state.tokens.curr); + nonadjacent(state.tokens.curr, state.tokens.next); + + this.left = left; + this.right = doFunction(undefined, undefined, false, left); + return this; + }; + return x; + } + function relation(s, f) { var x = symbol(s, 100); + x.led = function (left) { - nobreaknonadjacent(prevtoken, token); - nonadjacent(token, nexttoken); + nobreaknonadjacent(state.tokens.prev, state.tokens.curr); + nonadjacent(state.tokens.curr, state.tokens.next); var right = expression(100); if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) { - warning("Use the isNaN function to compare with NaN.", this); + warning("W019", this); } else if (f) { f.apply(this, [left, right]); } + + if (!left || !right) { + quit("E041", state.tokens.curr.line); + } + if (left.id === "!") { - warning("Confusing use of '{a}'.", left, "!"); + warning("W018", left, "!"); } + if (right.id === "!") { - warning("Confusing use of '{a}'.", right, "!"); + warning("W018", right, "!"); } + this.left = left; this.right = right; return this; @@ -4174,58 +4314,71 @@ klass: return x; } - function isPoorRelation(node) { return node && ((node.type === "(number)" && +node.value === 0) || (node.type === "(string)" && node.value === "") || - (node.type === "null" && !option.eqnull) || + (node.type === "null" && !state.option.eqnull) || node.type === "true" || node.type === "false" || node.type === "undefined"); } - function assignop(s) { symbol(s, 20).exps = true; return infix(s, function (left, that) { that.left = left; - if (predefined[left.value] === false && - scope[left.value]["(global)"] === true) { - warning("Read only.", left); - } else if (left["function"]) { - warning("'{a}' is a function.", left, left.value); - } - if (left) { - if (option.esnext && funct[left.value] === "const") { - warning("Attempting to override '{a}' which is a constant", left, left.value); + if (predefined[left.value] === false && + scope[left.value]["(global)"] === true) { + warning("W020", left); + } else if (left["function"]) { + warning("W021", left, left.value); } - if (left.id === "." || left.id === "[") { - if (!left.left || left.left.value === "arguments") { - warning("Bad assignment.", that); + if (funct[left.value] === "const") { + error("E013", left, left.value); + } + + if (left.id === ".") { + if (!left.left) { + warning("E031", that); + } else if (left.left.value === "arguments" && !state.directive["use strict"]) { + warning("E031", that); + } + + that.right = expression(19); + return that; + } else if (left.id === "[") { + if (state.tokens.curr.left.first) { + state.tokens.curr.left.first.forEach(function (t) { + if (funct[t.value] === "const") { + error("E013", t, t.value); + } + }); + } else if (!left.left) { + warning("E031", that); + } else if (left.left.value === "arguments" && !state.directive["use strict"]) { + warning("E031", that); } that.right = expression(19); return that; - } else if (left.identifier && !left.reserved) { + } else if (left.identifier && !isReserved(left)) { if (funct[left.value] === "exception") { - warning("Do not assign to the exception parameter.", left); + warning("W022", left); } that.right = expression(19); return that; } - if (left === syntax["function"]) { - warning( -"Expected an identifier in an assignment and instead saw a function invocation.", - token); + if (left === state.syntax["function"]) { + warning("W023", state.tokens.curr); } } - error("Bad assignment.", that); + error("E031", that); }, 20); } @@ -4234,8 +4387,8 @@ klass: var x = symbol(s, p); reserveName(x); x.led = (typeof f === "function") ? f : function (left) { - if (option.bitwise) { - warning("Unexpected use of '{a}'.", this, this.id); + if (state.option.bitwise) { + warning("W016", this, this.id); } this.left = left; this.right = expression(p); @@ -4248,72 +4401,91 @@ klass: function bitwiseassignop(s) { symbol(s, 20).exps = true; return infix(s, function (left, that) { - if (option.bitwise) { - warning("Unexpected use of '{a}'.", that, that.id); + if (state.option.bitwise) { + warning("W016", that, that.id); } - nonadjacent(prevtoken, token); - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.prev, state.tokens.curr); + nonadjacent(state.tokens.curr, state.tokens.next); if (left) { if (left.id === "." || left.id === "[" || - (left.identifier && !left.reserved)) { + (left.identifier && !isReserved(left))) { expression(19); return that; } - if (left === syntax["function"]) { - warning( -"Expected an identifier in an assignment, and instead saw a function invocation.", - token); + if (left === state.syntax["function"]) { + warning("W023", state.tokens.curr); } return that; } - error("Bad assignment.", that); + error("E031", that); }, 20); } function suffix(s) { var x = symbol(s, 150); + x.led = function (left) { - if (option.plusplus) { - warning("Unexpected use of '{a}'.", this, this.id); - } else if ((!left.identifier || left.reserved) && - left.id !== "." && left.id !== "[") { - warning("Bad operand.", this); + if (state.option.plusplus) { + warning("W016", this, this.id); + } else if ((!left.identifier || isReserved(left)) && left.id !== "." && left.id !== "[") { + warning("W017", this); } + this.left = left; return this; }; return x; } - function optionalidentifier(fnparam) { - if (nexttoken.identifier) { - advance(); - if (token.reserved && !option.es5) { - if (!fnparam || token.value !== "undefined") { - warning("Expected an identifier and instead saw '{a}' (a reserved word).", - token, token.id); - } - } - return token.value; + + function optionalidentifier(fnparam, prop) { + if (!state.tokens.next.identifier) { + return; } + + advance(); + + var curr = state.tokens.curr; + var meta = curr.meta || {}; + var val = state.tokens.curr.value; + + if (!isReserved(curr)) { + return val; + } + + if (prop) { + if (state.option.inES5() || meta.isFutureReservedWord) { + return val; + } + } + + if (fnparam && val === "undefined") { + return val; + } + if (prop && !api.getCache("displayed:I002")) { + api.setCache("displayed:I002", true); + warning("I002"); + } + + warning("W024", state.tokens.curr, state.tokens.curr.id); + return val; } - function identifier(fnparam) { - var i = optionalidentifier(fnparam); + function identifier(fnparam, prop) { + var i = optionalidentifier(fnparam, prop); if (i) { return i; } - if (token.id === "function" && nexttoken.id === "(") { - warning("Missing name in function declaration."); + if (state.tokens.curr.id === "function" && state.tokens.next.id === "(") { + warning("W025"); } else { - error("Expected an identifier and instead saw '{a}'.", - nexttoken, nexttoken.value); + error("E030", state.tokens.next, state.tokens.next.value); } } function reachable(s) { var i = 0, t; - if (nexttoken.id !== ";" || noreach) { + if (state.tokens.next.id !== ";" || noreach) { return; } for (;;) { @@ -4323,14 +4495,15 @@ klass: } if (t.id !== "(endline)") { if (t.id === "function") { - if (!option.latedef) { + if (!state.option.latedef) { break; } - warning( -"Inner functions should be listed at the top of the outer function.", t); + + warning("W026", t); break; } - warning("Unreachable '{a}' after '{b}'.", t, t.value, s); + + warning("W027", t, t.value, s); break; } i += 1; @@ -4339,29 +4512,46 @@ klass: function statement(noindent) { - var i = indent, r, s = scope, t = nexttoken; + var values; + var i = indent, r, s = scope, t = state.tokens.next; if (t.id === ";") { advance(";"); return; } + var res = isReserved(t); - if (t.identifier && !t.reserved && peek().id === ":") { + if (res && t.meta && t.meta.isFutureReservedWord && peek().id === ":") { + warning("W024", t, t.id); + res = false; + } + if (_.has(["[", "{"], t.value)) { + if (lookupBlockType().isDestAssign) { + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "destructuring expression"); + } + values = destructuringExpression(); + values.forEach(function (tok) { + isundef(funct, "W117", tok.token, tok.id); + }); + advance("="); + destructuringExpressionMatch(values, expression(5, true)); + advance(";"); + return; + } + } + if (t.identifier && !res && peek().id === ":") { advance(); advance(":"); scope = Object.create(s); addlabel(t.value, "label"); - if (!nexttoken.labelled && nexttoken.value !== "{") { - warning("Label '{a}' on {b} statement.", nexttoken, t.value, nexttoken.value); + if (!state.tokens.next.labelled && state.tokens.next.value !== "{") { + warning("W028", state.tokens.next, t.value, state.tokens.next.value); } - if (jx.test(t.value + ":")) { - warning("Label '{a}' looks like a javascript url.", t, t.value); - } - - nexttoken.label = t.value; - t = nexttoken; + state.tokens.next.label = t.value; + t = state.tokens.next; } if (t.id === "{") { @@ -4375,28 +4565,23 @@ klass: r = expression(0, true); if (!t.block) { - if (!option.expr && (!r || !r.exps)) { - warning("Expected an assignment or function call and instead saw an expression.", - token); - } else if (option.nonew && r.id === "(" && r.left.id === "new") { - warning("Do not use 'new' for side effects.", t); + if (!state.option.expr && (!r || !r.exps)) { + warning("W030", state.tokens.curr); + } else if (state.option.nonew && r && r.left && r.id === "(" && r.left.id === "new") { + warning("W031", t); } - if (nexttoken.id === ",") { - return comma(); - } - - if (nexttoken.id !== ";") { - if (!option.asi) { - if (!option.lastsemic || nexttoken.id !== "}" || - nexttoken.line !== token.line) { - warningAt("Missing semicolon.", token.line, token.character); + if (state.tokens.next.id !== ";") { + if (!state.option.asi) { + if (!state.option.lastsemic || state.tokens.next.id !== "}" || + state.tokens.next.line !== state.tokens.curr.line) { + warningAt("W033", state.tokens.curr.line, state.tokens.curr.character); } } } else { - adjacent(token, nexttoken); + adjacent(state.tokens.curr, state.tokens.next); advance(";"); - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.curr, state.tokens.next); } } @@ -4409,15 +4594,17 @@ klass: function statements(startLine) { var a = [], p; - while (!nexttoken.reach && nexttoken.id !== "(end)") { - if (nexttoken.id === ";") { + while (!state.tokens.next.reach && state.tokens.next.id !== "(end)") { + if (state.tokens.next.id === ";") { p = peek(); - if (!p || p.id !== "(") { - warning("Unnecessary semicolon."); + + if (!p || (p.id !== "(" && p.id !== "[")) { + warning("W032"); } + advance(";"); } else { - a.push(statement(startLine === nexttoken.line)); + a.push(statement(startLine === state.tokens.next.line)); } } return a; @@ -4426,7 +4613,7 @@ klass: var i, p, pn; for (;;) { - if (nexttoken.id === "(string)") { + if (state.tokens.next.id === "(string)") { p = peek(0); if (p.id === "(endline)") { i = 1; @@ -4441,28 +4628,28 @@ klass: pn.id !== "}") { break; } - warning("Missing semicolon.", nexttoken); + warning("W033", state.tokens.next); } else { p = pn; } } else if (p.id === "}") { - warning("Missing semicolon.", p); + warning("W033", p); } else if (p.id !== ";") { break; } indentation(); advance(); - if (directive[token.value]) { - warning("Unnecessary directive \"{a}\".", token, token.value); + if (state.directive[state.tokens.curr.value]) { + warning("W034", state.tokens.curr, state.tokens.curr.value); } - if (token.value === "use strict") { - if (!option["(explicitNewcap)"]) - option.newcap = true; - option.undef = true; + if (state.tokens.curr.value === "use strict") { + if (!state.option["(explicitNewcap)"]) + state.option.newcap = true; + state.option.undef = true; } - directive[token.value] = true; + state.directive[state.tokens.curr.value] = true; if (p.id === ";") { advance(";"); @@ -4472,7 +4659,7 @@ klass: break; } } - function block(ordinary, stmt, isfunc) { + function block(ordinary, stmt, isfunc, isfatarrow) { var a, b = inblock, old_indent = indent, @@ -4484,37 +4671,39 @@ klass: inblock = ordinary; - if (!ordinary || !option.funcscope) + if (!ordinary || !state.option.funcscope) scope = Object.create(scope); - nonadjacent(token, nexttoken); - t = nexttoken; + nonadjacent(state.tokens.curr, state.tokens.next); + t = state.tokens.next; var metrics = funct["(metrics)"]; metrics.nestedBlockDepth += 1; metrics.verifyMaxNestedBlockDepthPerFunction(); - if (nexttoken.id === "{") { + if (state.tokens.next.id === "{") { advance("{"); - line = token.line; - if (nexttoken.id !== "}") { - indent += option.indent; - while (!ordinary && nexttoken.from > indent) { - indent += option.indent; + funct["(blockscope)"].stack(); + + line = state.tokens.curr.line; + if (state.tokens.next.id !== "}") { + indent += state.option.indent; + while (!ordinary && state.tokens.next.from > indent) { + indent += state.option.indent; } if (isfunc) { m = {}; - for (d in directive) { - if (is_own(directive, d)) { - m[d] = directive[d]; + for (d in state.directive) { + if (_.has(state.directive, d)) { + m[d] = state.directive[d]; } } directives(); - if (option.strict && funct["(context)"]["(global)"]) { - if (!m["use strict"] && !directive["use strict"]) { - warning("Missing \"use strict\" statement."); + if (state.option.strict && funct["(context)"]["(global)"]) { + if (!m["use strict"] && !state.directive["use strict"]) { + warning("E007"); } } } @@ -4524,37 +4713,65 @@ klass: metrics.statementCount += a.length; if (isfunc) { - directive = m; + state.directive = m; } - indent -= option.indent; - if (line !== nexttoken.line) { + indent -= state.option.indent; + if (line !== state.tokens.next.line) { indentation(); } - } else if (line !== nexttoken.line) { + } else if (line !== state.tokens.next.line) { indentation(); } advance("}", t); + + funct["(blockscope)"].unstack(); + indent = old_indent; } else if (!ordinary) { - error("Expected '{a}' and instead saw '{b}'.", - nexttoken, "{", nexttoken.value); + if (isfunc) { + m = {}; + if (stmt && !isfatarrow && !state.option.inMoz(true)) { + error("W118", state.tokens.curr, "function closure expressions"); + } + + if (!stmt) { + for (d in state.directive) { + if (_.has(state.directive, d)) { + m[d] = state.directive[d]; + } + } + } + expression(5); + + if (state.option.strict && funct["(context)"]["(global)"]) { + if (!m["use strict"] && !state.directive["use strict"]) { + warning("E007"); + } + } + } else { + error("E021", state.tokens.next, "{", state.tokens.next.value); + } } else { - if (!stmt || option.curly) - warning("Expected '{a}' and instead saw '{b}'.", - nexttoken, "{", nexttoken.value); + funct["(nolet)"] = true; + + if (!stmt || state.option.curly) { + warning("W116", state.tokens.next, "{", state.tokens.next.value); + } noreach = true; - indent += option.indent; - a = [statement(nexttoken.line === token.line)]; - indent -= option.indent; + indent += state.option.indent; + a = [statement(state.tokens.next.line === state.tokens.curr.line)]; + indent -= state.option.indent; noreach = false; + + delete funct["(nolet)"]; } funct["(verb)"] = null; - if (!ordinary || !option.funcscope) scope = s; + if (!ordinary || !state.option.funcscope) scope = s; inblock = b; - if (ordinary && option.noempty && (!a || a.length === 0)) { - warning("Empty block."); + if (ordinary && state.option.noempty && (!a || a.length === 0)) { + warning("W035"); } metrics.nestedBlockDepth -= 1; return a; @@ -4563,7 +4780,7 @@ klass: function countMember(m) { if (membersOnly && typeof membersOnly[m] !== "boolean") { - warning("Unexpected /*member '{a}'.", token, m); + warning("W036", state.tokens.curr, m); } if (typeof member[m] === "number") { member[m] += 1; @@ -4573,8 +4790,8 @@ klass: } - function note_implied(token) { - var name = token.value, line = token.line, a = implied[name]; + function note_implied(tkn) { + var name = tkn.value, line = tkn.line, a = implied[name]; if (typeof a === "function") { a = false; } @@ -4595,7 +4812,7 @@ klass: return this; }); - syntax["(identifier)"] = { + state.syntax["(identifier)"] = { type: "(identifier)", lbp: 0, identifier: true, @@ -4613,33 +4830,42 @@ klass: s = funct; funct = f; } - if (funct === s) { - switch (funct[v]) { + var block; + if (_.has(funct, "(blockscope)")) { + block = funct["(blockscope)"].getlabel(v); + } + if (funct === s || block) { + switch (block ? block[v]["(type)"] : funct[v]) { case "unused": - funct[v] = "var"; + if (block) block[v]["(type)"] = "var"; + else funct[v] = "var"; break; case "unction": - funct[v] = "function"; + if (block) block[v]["(type)"] = "function"; + else funct[v] = "function"; this["function"] = true; break; case "function": this["function"] = true; break; case "label": - warning("'{a}' is a statement label.", token, v); + warning("W037", state.tokens.curr, v); break; } } else if (funct["(global)"]) { - if (option.undef && typeof predefined[v] !== "boolean") { + if (typeof predefined[v] !== "boolean") { if (!(anonname === "typeof" || anonname === "delete") || - (nexttoken && (nexttoken.value === "." || nexttoken.value === "["))) { + (state.tokens.next && (state.tokens.next.value === "." || + state.tokens.next.value === "["))) { - isundef(funct, "'{a}' is not defined.", token, v); + if (!funct["(comparray)"].check(v)) { + isundef(funct, "W117", state.tokens.curr, v); + } } } - note_implied(token); + note_implied(state.tokens.curr); } else { switch (funct[v]) { @@ -4647,10 +4873,10 @@ klass: case "function": case "var": case "unused": - warning("'{a}' used out of scope.", token, v); + warning("W038", state.tokens.curr, v); break; case "label": - warning("'{a}' is a statement label.", token, v); + warning("W037", state.tokens.curr, v); break; case "outer": case "global": @@ -4659,19 +4885,17 @@ klass: if (s === true) { funct[v] = true; } else if (s === null) { - warning("'{a}' is not allowed.", token, v); - note_implied(token); + warning("W039", state.tokens.curr, v); + note_implied(state.tokens.curr); } else if (typeof s !== "object") { - if (option.undef) { - if (!(anonname === "typeof" || anonname === "delete") || - (nexttoken && - (nexttoken.value === "." || nexttoken.value === "["))) { + if (!(anonname === "typeof" || anonname === "delete") || + (state.tokens.next && + (state.tokens.next.value === "." || state.tokens.next.value === "["))) { - isundef(funct, "'{a}' is not defined.", token, v); - } + isundef(funct, "W117", state.tokens.curr, v); } funct[v] = true; - note_implied(token); + note_implied(state.tokens.curr); } else { switch (s[v]) { case "function": @@ -4689,7 +4913,7 @@ klass: funct[v] = s["(global)"] ? "global" : "outer"; break; case "label": - warning("'{a}' is a statement label.", token, v); + warning("W037", state.tokens.curr, v); } } } @@ -4697,8 +4921,7 @@ klass: return this; }, led: function () { - error("Expected an operator and instead saw '{a}'.", - nexttoken, nexttoken.value); + error("E033", state.tokens.next, state.tokens.next.value); } }; @@ -4709,10 +4932,6 @@ klass: delim("(endline)"); delim("(begin)"); delim("(end)").reach = true; - delim(""); delim("(error)").reach = true; delim("}").reach = true; delim(")"); @@ -4721,17 +4940,16 @@ klass: delim("'").reach = true; delim(";"); delim(":").reach = true; - delim(","); delim("#"); - delim("@"); + reserve("else"); reserve("case").reach = true; reserve("catch"); reserve("default").reach = true; reserve("finally"); reservevar("arguments", function (x) { - if (directive["use strict"] && funct["(global)"]) { - warning("Strict violation.", x); + if (state.directive["use strict"] && funct["(global)"]) { + warning("E008", x); } }); reservevar("eval"); @@ -4739,27 +4957,46 @@ klass: reservevar("Infinity"); reservevar("null"); reservevar("this", function (x) { - if (directive["use strict"] && !option.validthis && ((funct["(statement)"] && + if (state.directive["use strict"] && !state.option.validthis && ((funct["(statement)"] && funct["(name)"].charAt(0) > "Z") || funct["(global)"])) { - warning("Possible strict violation.", x); + warning("W040", x); } }); reservevar("true"); reservevar("undefined"); + assignop("=", "assign", 20); assignop("+=", "assignadd", 20); assignop("-=", "assignsub", 20); assignop("*=", "assignmult", 20); assignop("/=", "assigndiv", 20).nud = function () { - error("A regular expression literal can be confused with '/='."); + error("E014"); }; assignop("%=", "assignmod", 20); + bitwiseassignop("&=", "assignbitand", 20); bitwiseassignop("|=", "assignbitor", 20); bitwiseassignop("^=", "assignbitxor", 20); bitwiseassignop("<<=", "assignshiftleft", 20); bitwiseassignop(">>=", "assignshiftright", 20); bitwiseassignop(">>>=", "assignshiftrightunsigned", 20); + infix(",", function (left, that) { + var expr; + that.exprs = [left]; + if (!comma({peek: true})) { + return that; + } + while (true) { + if (!(expr = expression(5))) { + break; + } + that.exprs.push(expr); + if (state.tokens.next.value !== "," || !comma()) { + break; + } + } + return that; + }, 5, true); infix("?", function (left, that) { that.left = left; that.right = expression(10); @@ -4774,31 +5011,28 @@ klass: bitwise("^", "bitxor", 80); bitwise("&", "bitand", 90); relation("==", function (left, right) { - var eqnull = option.eqnull && (left.value === "null" || right.value === "null"); + var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null"); - if (!eqnull && option.eqeqeq) - warning("Expected '{a}' and instead saw '{b}'.", this, "===", "=="); + if (!eqnull && state.option.eqeqeq) + warning("W116", this, "===", "=="); else if (isPoorRelation(left)) - warning("Use '{a}' to compare with '{b}'.", this, "===", left.value); + warning("W041", this, "===", left.value); else if (isPoorRelation(right)) - warning("Use '{a}' to compare with '{b}'.", this, "===", right.value); + warning("W041", this, "===", right.value); return this; }); relation("==="); relation("!=", function (left, right) { - var eqnull = option.eqnull && + var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null"); - if (!eqnull && option.eqeqeq) { - warning("Expected '{a}' and instead saw '{b}'.", - this, "!==", "!="); + if (!eqnull && state.option.eqeqeq) { + warning("W116", this, "!==", "!="); } else if (isPoorRelation(left)) { - warning("Use '{a}' to compare with '{b}'.", - this, "!==", left.value); + warning("W041", this, "!==", left.value); } else if (isPoorRelation(right)) { - warning("Use '{a}' to compare with '{b}'.", - this, "!==", right.value); + warning("W041", this, "!==", right.value); } return this; }); @@ -4817,8 +5051,8 @@ klass: if (left && right && left.id === "(string)" && right.id === "(string)") { left.value += right.value; left.character = right.character; - if (!option.scripturl && jx.test(left.value)) { - warning("JavaScript URL.", left); + if (!state.option.scripturl && reg.javascriptURL.test(left.value)) { + warning("W050", left); } return left; } @@ -4828,13 +5062,13 @@ klass: }, 130); prefix("+", "num"); prefix("+++", function () { - warning("Confusing pluses."); + warning("W007"); this.right = expression(150); this.arity = "unary"; return this; }); infix("+++", function (left) { - warning("Confusing pluses."); + warning("W007"); this.left = left; this.right = expression(130); return this; @@ -4842,13 +5076,13 @@ klass: infix("-", "sub", 130); prefix("-", "neg"); prefix("---", function () { - warning("Confusing minuses."); + warning("W006"); this.right = expression(150); this.arity = "unary"; return this; }); infix("---", function (left) { - warning("Confusing minuses."); + warning("W006"); this.left = left; this.right = expression(130); return this; @@ -4859,23 +5093,34 @@ klass: suffix("++", "postinc"); prefix("++", "preinc"); - syntax["++"].exps = true; + state.syntax["++"].exps = true; suffix("--", "postdec"); prefix("--", "predec"); - syntax["--"].exps = true; + state.syntax["--"].exps = true; prefix("delete", function () { - var p = expression(0); + var p = expression(5); if (!p || (p.id !== "." && p.id !== "[")) { - warning("Variables should not be deleted."); + warning("W051"); } this.first = p; return this; }).exps = true; prefix("~", function () { - if (option.bitwise) { - warning("Unexpected '{a}'.", this, "~"); + if (state.option.bitwise) { + warning("W052", this, "~"); + } + expression(150); + return this; + }); + + prefix("...", function () { + if (!state.option.inESNext()) { + warning("W104", this, "spread/rest operator"); + } + if (!state.tokens.next.identifier) { + error("E030", state.tokens.next, state.tokens.next.value); } expression(150); return this; @@ -4884,11 +5129,17 @@ klass: prefix("!", function () { this.right = expression(150); this.arity = "unary"; + + if (!this.right) { // '!' followed by nothing? Give up. + quit("E041", this.line || 0); + } + if (bang[this.right.id] === true) { - warning("Confusing use of '{a}'.", this, "!"); + warning("W018", this, "!"); } return this; }); + prefix("typeof", "typeof"); prefix("new", function () { var c = expression(155), i; @@ -4901,11 +5152,11 @@ klass: case "Boolean": case "Math": case "JSON": - warning("Do not use {a} as a constructor.", prevtoken, c.value); + warning("W053", state.tokens.prev, c.value); break; case "Function": - if (!option.evil) { - warning("The Function constructor is eval."); + if (!state.option.evil) { + warning("W054"); } break; case "Date": @@ -4914,102 +5165,113 @@ klass: default: if (c.id !== "function") { i = c.value.substr(0, 1); - if (option.newcap && (i < "A" || i > "Z") && !is_own(global, c.value)) { - warning("A constructor name should start with an uppercase letter.", - token); + if (state.option.newcap && (i < "A" || i > "Z") && !_.has(global, c.value)) { + warning("W055", state.tokens.curr); } } } } else { if (c.id !== "." && c.id !== "[" && c.id !== "(") { - warning("Bad constructor.", token); + warning("W056", state.tokens.curr); } } } else { - if (!option.supernew) - warning("Weird construction. Delete 'new'.", this); + if (!state.option.supernew) + warning("W057", this); } - adjacent(token, nexttoken); - if (nexttoken.id !== "(" && !option.supernew) { - warning("Missing '()' invoking a constructor.", - token, token.value); + adjacent(state.tokens.curr, state.tokens.next); + if (state.tokens.next.id !== "(" && !state.option.supernew) { + warning("W058", state.tokens.curr, state.tokens.curr.value); } this.first = c; return this; }); - syntax["new"].exps = true; + state.syntax["new"].exps = true; prefix("void").exps = true; infix(".", function (left, that) { - adjacent(prevtoken, token); + adjacent(state.tokens.prev, state.tokens.curr); nobreak(); - var m = identifier(); + var m = identifier(false, true); + if (typeof m === "string") { countMember(m); } + that.left = left; that.right = m; + + if (m && m === "hasOwnProperty" && state.tokens.next.value === "=") { + warning("W001"); + } + if (left && left.value === "arguments" && (m === "callee" || m === "caller")) { - if (option.noarg) - warning("Avoid arguments.{a}.", left, m); - else if (directive["use strict"]) - error("Strict violation."); - } else if (!option.evil && left && left.value === "document" && + if (state.option.noarg) + warning("W059", left, m); + else if (state.directive["use strict"]) + error("E008"); + } else if (!state.option.evil && left && left.value === "document" && (m === "write" || m === "writeln")) { - warning("document.write can be a form of eval.", left); + warning("W060", left); } - if (!option.evil && (m === "eval" || m === "execScript")) { - warning("eval is evil."); + + if (!state.option.evil && (m === "eval" || m === "execScript")) { + warning("W061"); } + return that; }, 160, true); infix("(", function (left, that) { - if (prevtoken.id !== "}" && prevtoken.id !== ")") { - nobreak(prevtoken, token); + if (state.tokens.prev.id !== "}" && state.tokens.prev.id !== ")") { + nobreak(state.tokens.prev, state.tokens.curr); } + nospace(); - if (option.immed && !left.immed && left.id === "function") { - warning("Wrap an immediate function invocation in parentheses " + - "to assist the reader in understanding that the expression " + - "is the result of a function, and not the function itself."); + if (state.option.immed && left && !left.immed && left.id === "function") { + warning("W062"); } - var n = 0, - p = []; + + var n = 0; + var p = []; + if (left) { if (left.type === "(identifier)") { if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { if ("Number String Boolean Date Object".indexOf(left.value) === -1) { if (left.value === "Math") { - warning("Math is not a function.", left); - } else if (option.newcap) { - warning("Missing 'new' prefix when invoking a constructor.", left); + warning("W063", left); + } else if (state.option.newcap) { + warning("W064", left); } } } } } - if (nexttoken.id !== ")") { + + if (state.tokens.next.id !== ")") { for (;;) { p[p.length] = expression(10); n += 1; - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } comma(); } } + advance(")"); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); + if (typeof left === "object") { if (left.value === "parseInt" && n === 1) { - warning("Missing radix parameter.", token); + warning("W065", state.tokens.curr); } - if (!option.evil) { + if (!state.option.evil) { if (left.value === "eval" || left.value === "Function" || left.value === "execScript") { - warning("eval is evil.", left); + warning("W061", left); if (p[0] && [0].id === "(string)") { addInternalSrc(left, p[0].value); @@ -5017,99 +5279,188 @@ klass: } else if (p[0] && p[0].id === "(string)" && (left.value === "setTimeout" || left.value === "setInterval")) { - warning( - "Implied eval is evil. Pass a function instead of a string.", left); + warning("W066", left); addInternalSrc(left, p[0].value); } else if (p[0] && p[0].id === "(string)" && left.value === "." && left.left.value === "window" && (left.right === "setTimeout" || left.right === "setInterval")) { - warning( - "Implied eval is evil. Pass a function instead of a string.", left); + warning("W066", left); addInternalSrc(left, p[0].value); } } if (!left.identifier && left.id !== "." && left.id !== "[" && left.id !== "(" && left.id !== "&&" && left.id !== "||" && left.id !== "?") { - warning("Bad invocation.", left); + warning("W067", left); } } + that.left = left; return that; }, 155, true).exps = true; prefix("(", function () { nospace(); - if (nexttoken.id === "function") { - nexttoken.immed = true; + var bracket, brackets = []; + var pn, pn1, i = 0; + var ret; + + do { + pn = peek(i); + i += 1; + pn1 = peek(i); + i += 1; + } while (pn.value !== ")" && pn1.value !== "=>" && pn1.value !== ";" && pn1.type !== "(end)"); + + if (state.tokens.next.id === "function") { + state.tokens.next.immed = true; } - var v = expression(0); - advance(")", this); - nospace(prevtoken, token); - if (option.immed && v.id === "function") { - if (nexttoken.id !== "(" && - (nexttoken.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) { - warning( -"Do not wrap function literals in parens unless they are to be immediately invoked.", - this); + + var exprs = []; + + if (state.tokens.next.id !== ")") { + for (;;) { + if (pn1.value === "=>" && state.tokens.next.value === "{") { + bracket = state.tokens.next; + bracket.left = destructuringExpression(); + brackets.push(bracket); + for (var t in bracket.left) { + exprs.push(bracket.left[t].token); + } + } else { + exprs.push(expression(5)); + } + if (state.tokens.next.id !== ",") { + break; + } + comma(); } } - return v; + advance(")", this); + nospace(state.tokens.prev, state.tokens.curr); + if (state.option.immed && exprs[0] && exprs[0].id === "function") { + if (state.tokens.next.id !== "(" && + (state.tokens.next.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) { + warning("W068", this); + } + } + + if (state.tokens.next.value === "=>") { + return exprs; + } + if (!exprs.length) { + return; + } + if (exprs.length > 1) { + ret = Object.create(state.syntax[","]); + ret.exprs = exprs; + } else { + ret = exprs[0]; + } + if (ret) { + ret.paren = true; + } + return ret; }); + application("=>"); + infix("[", function (left, that) { - nobreak(prevtoken, token); + nobreak(state.tokens.prev, state.tokens.curr); nospace(); - var e = expression(0), s; + var e = expression(5), s; if (e && e.type === "(string)") { - if (!option.evil && (e.value === "eval" || e.value === "execScript")) { - warning("eval is evil.", that); + if (!state.option.evil && (e.value === "eval" || e.value === "execScript")) { + warning("W061", that); } + countMember(e.value); - if (!option.sub && ix.test(e.value)) { - s = syntax[e.value]; - if (!s || !s.reserved) { - warning("['{a}'] is better written in dot notation.", - prevtoken, e.value); + if (!state.option.sub && reg.identifier.test(e.value)) { + s = state.syntax[e.value]; + if (!s || !isReserved(s)) { + warning("W069", state.tokens.prev, e.value); } } } advance("]", that); - nospace(prevtoken, token); + + if (e && e.value === "hasOwnProperty" && state.tokens.next.value === "=") { + warning("W001"); + } + + nospace(state.tokens.prev, state.tokens.curr); that.left = left; that.right = e; return that; }, 160, true); - prefix("[", function () { - var b = token.line !== nexttoken.line; - this.first = []; - if (b) { - indent += option.indent; - if (nexttoken.from === indent + option.indent) { - indent += option.indent; + function comprehensiveArrayExpression() { + var res = {}; + res.exps = true; + funct["(comparray)"].stack(); + + res.right = expression(5); + advance("for"); + if (state.tokens.next.value === "each") { + advance("each"); + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.curr, "for each"); } } - while (nexttoken.id !== "(end)") { - while (nexttoken.id === ",") { - if (!option.es5) - warning("Extra comma."); + advance("("); + funct["(comparray)"].setState("define"); + res.left = expression(5); + advance(")"); + if (state.tokens.next.value === "if") { + advance("if"); + advance("("); + funct["(comparray)"].setState("filter"); + res.filter = expression(5); + advance(")"); + } + advance("]"); + funct["(comparray)"].unstack(); + return res; + } + + prefix("[", function () { + var blocktype = lookupBlockType(true); + if (blocktype.isCompArray) { + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.curr, "array comprehension"); + } + return comprehensiveArrayExpression(); + } else if (blocktype.isDestAssign && !state.option.inESNext()) { + warning("W104", state.tokens.curr, "destructuring assignment"); + } + var b = state.tokens.curr.line !== state.tokens.next.line; + this.first = []; + if (b) { + indent += state.option.indent; + if (state.tokens.next.from === indent + state.option.indent) { + indent += state.option.indent; + } + } + while (state.tokens.next.id !== "(end)") { + while (state.tokens.next.id === ",") { + if (!state.option.inES5()) + warning("W070"); advance(","); } - if (nexttoken.id === "]") { + if (state.tokens.next.id === "]") { break; } - if (b && token.line !== nexttoken.line) { + if (b && state.tokens.curr.line !== state.tokens.next.line) { indentation(); } this.first.push(expression(10)); - if (nexttoken.id === ",") { - comma(); - if (nexttoken.id === "]" && !option.es5) { - warning("Extra comma.", token); + if (state.tokens.next.id === ",") { + comma({ allowTrailing: true }); + if (state.tokens.next.id === "]" && !state.option.inES5(true)) { + warning("W070", state.tokens.curr); break; } } else { @@ -5117,7 +5468,7 @@ klass: } } if (b) { - indent -= option.indent; + indent -= state.option.indent; indentation(); } advance("]", this); @@ -5126,71 +5477,139 @@ klass: function property_name() { - var id = optionalidentifier(true); + var id = optionalidentifier(false, true); + if (!id) { - if (nexttoken.id === "(string)") { - id = nexttoken.value; + if (state.tokens.next.id === "(string)") { + id = state.tokens.next.value; advance(); - } else if (nexttoken.id === "(number)") { - id = nexttoken.value.toString(); + } else if (state.tokens.next.id === "(number)") { + id = state.tokens.next.value.toString(); advance(); } } + + if (id === "hasOwnProperty") { + warning("W001"); + } + return id; } - function functionparams() { - var next = nexttoken; + function functionparams(parsed) { + var curr, next; var params = []; var ident; + var tokens = []; + var t; + + if (parsed) { + if (parsed instanceof Array) { + for (var i in parsed) { + curr = parsed[i]; + if (_.contains(["{", "["], curr.id)) { + for (t in curr.left) { + t = tokens[t]; + if (t.id) { + params.push(t.id); + addlabel(t.id, "unused", t.token); + } + } + } else if (curr.value === "...") { + if (!state.option.inESNext()) { + warning("W104", curr, "spread/rest operator"); + } + continue; + } else { + addlabel(curr.value, "unused", curr); + } + } + return params; + } else { + if (parsed.identifier === true) { + addlabel(parsed.value, "unused", parsed); + return [parsed]; + } + } + } + + next = state.tokens.next; advance("("); nospace(); - if (nexttoken.id === ")") { + if (state.tokens.next.id === ")") { advance(")"); return; } for (;;) { - ident = identifier(true); - params.push(ident); - addlabel(ident, "unused", token); - if (nexttoken.id === ",") { + if (_.contains(["{", "["], state.tokens.next.id)) { + tokens = destructuringExpression(); + for (t in tokens) { + t = tokens[t]; + if (t.id) { + params.push(t.id); + addlabel(t.id, "unused", t.token); + } + } + } else if (state.tokens.next.value === "...") { + if (!state.option.inESNext()) { + warning("W104", state.tokens.next, "spread/rest operator"); + } + advance("..."); + nospace(); + ident = identifier(true); + params.push(ident); + addlabel(ident, "unused", state.tokens.curr); + } else { + ident = identifier(true); + params.push(ident); + addlabel(ident, "unused", state.tokens.curr); + } + if (state.tokens.next.id === ",") { comma(); } else { advance(")", next); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); return params; } } } - function doFunction(name, statement) { + function doFunction(name, statement, generator, fatarrowparams) { var f; - var oldOption = option; + var oldOption = state.option; + var oldIgnored = state.ignored; var oldScope = scope; - option = Object.create(option); + state.option = Object.create(state.option); + state.ignored = Object.create(state.ignored); scope = Object.create(scope); funct = { - "(name)" : name || "\"" + anonname + "\"", - "(line)" : nexttoken.line, - "(character)": nexttoken.character, - "(context)" : funct, - "(breakage)" : 0, - "(loopage)" : 0, - "(metrics)" : createMetrics(nexttoken), - "(scope)" : scope, - "(statement)": statement, - "(tokens)" : {} + "(name)" : name || "\"" + anonname + "\"", + "(line)" : state.tokens.next.line, + "(character)" : state.tokens.next.character, + "(context)" : funct, + "(breakage)" : 0, + "(loopage)" : 0, + "(metrics)" : createMetrics(state.tokens.next), + "(scope)" : scope, + "(statement)" : statement, + "(tokens)" : {}, + "(blockscope)": funct["(blockscope)"], + "(comparray)" : funct["(comparray)"] }; + if (generator) { + funct["(generator)"] = true; + } + f = funct; - token.funct = funct; + state.tokens.curr.funct = funct; functions.push(funct); @@ -5198,18 +5617,25 @@ klass: addlabel(name, "function"); } - funct["(params)"] = functionparams(); + funct["(params)"] = functionparams(fatarrowparams); + funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]); - block(false, false, true); + block(false, true, true, fatarrowparams ? true:false); + + if (generator && funct["(generator)"] !== "yielded") { + error("E047", state.tokens.curr); + } funct["(metrics)"].verifyMaxStatementsPerFunction(); funct["(metrics)"].verifyMaxComplexityPerFunction(); + funct["(unusedOption)"] = state.option.unused; scope = oldScope; - option = oldOption; - funct["(last)"] = token.line; - funct["(lastcharacter)"] = token.character; + state.option = oldOption; + state.ignored = oldIgnored; + funct["(last)"] = state.tokens.curr.line; + funct["(lastcharacter)"] = state.tokens.curr.character; funct = funct["(context)"]; return f; @@ -5221,37 +5647,33 @@ klass: nestedBlockDepth: -1, ComplexityCount: 1, verifyMaxStatementsPerFunction: function () { - if (option.maxstatements && - this.statementCount > option.maxstatements) { - var message = "Too many statements per function (" + this.statementCount + ")."; - warning(message, functionStartToken); + if (state.option.maxstatements && + this.statementCount > state.option.maxstatements) { + warning("W071", functionStartToken, this.statementCount); } }, verifyMaxParametersPerFunction: function (params) { params = params || []; - if (option.maxparams && params.length > option.maxparams) { - var message = "Too many parameters per function (" + params.length + ")."; - warning(message, functionStartToken); + if (state.option.maxparams && params.length > state.option.maxparams) { + warning("W072", functionStartToken, params.length); } }, verifyMaxNestedBlockDepthPerFunction: function () { - if (option.maxdepth && + if (state.option.maxdepth && this.nestedBlockDepth > 0 && - this.nestedBlockDepth === option.maxdepth + 1) { - var message = "Blocks are nested too deeply (" + this.nestedBlockDepth + ")."; - warning(message); + this.nestedBlockDepth === state.option.maxdepth + 1) { + warning("W073", null, this.nestedBlockDepth); } }, verifyMaxComplexityPerFunction: function () { - var max = option.maxcomplexity; + var max = state.option.maxcomplexity; var cc = this.ComplexityCount; if (max && cc > max) { - var message = "Cyclomatic complexity is too high per function (" + cc + ")."; - warning(message, functionStartToken); + warning("W074", functionStartToken, cc); } } }; @@ -5261,236 +5683,406 @@ klass: funct["(metrics)"].ComplexityCount += 1; } + function checkCondAssignment(expr) { + var id, paren; + if (expr) { + id = expr.id; + paren = expr.paren; + if (id === "," && (expr = expr.exprs[expr.exprs.length - 1])) { + id = expr.id; + paren = paren || expr.paren; + } + } + switch (id) { + case "=": + case "+=": + case "-=": + case "*=": + case "%=": + case "&=": + case "|=": + case "^=": + case "/=": + if (!paren && !state.option.boss) { + warning("W084"); + } + } + } + (function (x) { - x.nud = function () { - var b, f, i, p, t; + x.nud = function (isclassdef) { + var b, f, i, p, t, g; var props = {}; // All properties, including accessors + var tag = ""; - function saveProperty(name, token) { - if (props[name] && is_own(props, name)) - warning("Duplicate member '{a}'.", nexttoken, i); + function saveProperty(name, tkn) { + if (props[name] && _.has(props, name)) + warning("W075", state.tokens.next, i); else props[name] = {}; props[name].basic = true; - props[name].basicToken = token; + props[name].basictkn = tkn; } - function saveSetter(name, token) { - if (props[name] && is_own(props, name)) { + function saveSetter(name, tkn) { + if (props[name] && _.has(props, name)) { if (props[name].basic || props[name].setter) - warning("Duplicate member '{a}'.", nexttoken, i); + warning("W075", state.tokens.next, i); } else { props[name] = {}; } props[name].setter = true; - props[name].setterToken = token; + props[name].setterToken = tkn; } function saveGetter(name) { - if (props[name] && is_own(props, name)) { + if (props[name] && _.has(props, name)) { if (props[name].basic || props[name].getter) - warning("Duplicate member '{a}'.", nexttoken, i); + warning("W075", state.tokens.next, i); } else { props[name] = {}; } props[name].getter = true; - props[name].getterToken = token; + props[name].getterToken = state.tokens.curr; } - b = token.line !== nexttoken.line; + b = state.tokens.curr.line !== state.tokens.next.line; if (b) { - indent += option.indent; - if (nexttoken.from === indent + option.indent) { - indent += option.indent; + indent += state.option.indent; + if (state.tokens.next.from === indent + state.option.indent) { + indent += state.option.indent; } } + for (;;) { - if (nexttoken.id === "}") { + if (state.tokens.next.id === "}") { break; } + if (b) { indentation(); } - if (nexttoken.value === "get" && peek().id !== ":") { + + if (isclassdef && state.tokens.next.value === "static") { + advance("static"); + tag = "static "; + } + + if (state.tokens.next.value === "get" && peek().id !== ":") { advance("get"); - if (!option.es5) { - error("get/set are ES5 features."); + + if (!state.option.inES5(!isclassdef)) { + error("E034"); } + i = property_name(); if (!i) { - error("Missing property name."); + error("E035"); } - saveGetter(i); - t = nexttoken; - adjacent(token, nexttoken); + if (isclassdef && i === "constructor") { + error("E049", state.tokens.next, "class getter method", i); + } + + saveGetter(tag + i); + t = state.tokens.next; + adjacent(state.tokens.curr, state.tokens.next); f = doFunction(); p = f["(params)"]; + if (p) { - warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i); + warning("W076", t, p[0], i); } - adjacent(token, nexttoken); - } else if (nexttoken.value === "set" && peek().id !== ":") { + + adjacent(state.tokens.curr, state.tokens.next); + } else if (state.tokens.next.value === "set" && peek().id !== ":") { advance("set"); - if (!option.es5) { - error("get/set are ES5 features."); + + if (!state.option.inES5(!isclassdef)) { + error("E034"); } + i = property_name(); if (!i) { - error("Missing property name."); + error("E035"); } - saveSetter(i, nexttoken); - t = nexttoken; - adjacent(token, nexttoken); + if (isclassdef && i === "constructor") { + error("E049", state.tokens.next, "class setter method", i); + } + + saveSetter(tag + i, state.tokens.next); + t = state.tokens.next; + adjacent(state.tokens.curr, state.tokens.next); f = doFunction(); p = f["(params)"]; + if (!p || p.length !== 1) { - warning("Expected a single parameter in set {a} function.", t, i); + warning("W077", t, i); } } else { + g = false; + if (state.tokens.next.value === "*" && state.tokens.next.type === "(punctuator)") { + if (!state.option.inESNext()) { + warning("W104", state.tokens.next, "generator functions"); + } + advance("*"); + g = true; + } i = property_name(); - saveProperty(i, nexttoken); + saveProperty(tag + i, state.tokens.next); + if (typeof i !== "string") { break; } - advance(":"); - nonadjacent(token, nexttoken); - expression(10); + + if (state.tokens.next.value === "(") { + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "concise methods"); + } + doFunction(i, undefined, g); + } else if (!isclassdef) { + advance(":"); + nonadjacent(state.tokens.curr, state.tokens.next); + expression(10); + } + } + if (isclassdef && i === "prototype") { + error("E049", state.tokens.next, "class method", i); } countMember(i); - if (nexttoken.id === ",") { - comma(); - if (nexttoken.id === ",") { - warning("Extra comma.", token); - } else if (nexttoken.id === "}" && !option.es5) { - warning("Extra comma.", token); + if (isclassdef) { + tag = ""; + continue; + } + if (state.tokens.next.id === ",") { + comma({ allowTrailing: true, property: true }); + if (state.tokens.next.id === ",") { + warning("W070", state.tokens.curr); + } else if (state.tokens.next.id === "}" && !state.option.inES5(true)) { + warning("W070", state.tokens.curr); } } else { break; } } if (b) { - indent -= option.indent; + indent -= state.option.indent; indentation(); } advance("}", this); - if (option.es5) { + if (state.option.inES5()) { for (var name in props) { - if (is_own(props, name) && props[name].setter && !props[name].getter) { - warning("Setter is defined without getter.", props[name].setterToken); + if (_.has(props, name) && props[name].setter && !props[name].getter) { + warning("W078", props[name].setterToken); } } } return this; }; x.fud = function () { - error("Expected to see a statement and instead saw a block.", token); + error("E036", state.tokens.curr); }; }(delim("{"))); - useESNextSyntax = function () { - var conststatement = stmt("const", function (prefix) { - var id, name, value; - - this.first = []; - for (;;) { - nonadjacent(token, nexttoken); - id = identifier(); - if (funct[id] === "const") { - warning("const '" + id + "' has already been declared"); + function destructuringExpression() { + var id, ids; + var identifiers = []; + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "destructuring expression"); + } + var nextInnerDE = function () { + var ident; + if (_.contains(["[", "{"], state.tokens.next.value)) { + ids = destructuringExpression(); + for (var id in ids) { + id = ids[id]; + identifiers.push({ id: id.id, token: id.token }); } - if (funct["(global)"] && predefined[id] === false) { - warning("Redefinition of '{a}'.", token, id); - } - addlabel(id, "const"); - if (prefix) { - break; - } - name = token; - this.first.push(token); - - if (nexttoken.id !== "=") { - warning("const " + - "'{a}' is initialized to 'undefined'.", token, id); - } - - if (nexttoken.id === "=") { - nonadjacent(token, nexttoken); - advance("="); - nonadjacent(token, nexttoken); - if (nexttoken.id === "undefined") { - warning("It is not necessary to initialize " + - "'{a}' to 'undefined'.", token, id); - } - if (peek(0).id === "=" && nexttoken.identifier) { - error("Constant {a} was not declared correctly.", - nexttoken, nexttoken.value); - } - value = expression(0); - name.first = value; - } - - if (nexttoken.id !== ",") { - break; - } - comma(); + } else if (state.tokens.next.value === ",") { + identifiers.push({ id: null, token: state.tokens.curr }); + } else { + ident = identifier(); + if (ident) + identifiers.push({ id: ident, token: state.tokens.curr }); } - return this; - }); - conststatement.exps = true; - }; + }; + if (state.tokens.next.value === "[") { + advance("["); + nextInnerDE(); + while (state.tokens.next.value !== "]") { + advance(","); + nextInnerDE(); + } + advance("]"); + } else if (state.tokens.next.value === "{") { + advance("{"); + id = identifier(); + if (state.tokens.next.value === ":") { + advance(":"); + nextInnerDE(); + } else { + identifiers.push({ id: id, token: state.tokens.curr }); + } + while (state.tokens.next.value !== "}") { + advance(","); + id = identifier(); + if (state.tokens.next.value === ":") { + advance(":"); + nextInnerDE(); + } else { + identifiers.push({ id: id, token: state.tokens.curr }); + } + } + advance("}"); + } + return identifiers; + } + function destructuringExpressionMatch(tokens, value) { + if (value.first) { + _.zip(tokens, value.first).forEach(function (val) { + var token = val[0]; + var value = val[1]; + if (token && value) { + token.first = value; + } else if (token && token.first && !value) { + warning("W080", token.first, token.first.value); + } /* else { + XXX value is discarded: wouldn't it need a warning ? + } */ + }); + } + } + var conststatement = stmt("const", function (prefix) { + var tokens, value; + var lone; + + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "const"); + } + + this.first = []; + for (;;) { + var names = []; + nonadjacent(state.tokens.curr, state.tokens.next); + if (_.contains(["{", "["], state.tokens.next.value)) { + tokens = destructuringExpression(); + lone = false; + } else { + tokens = [ { id: identifier(), token: state.tokens.curr } ]; + lone = true; + } + for (var t in tokens) { + t = tokens[t]; + if (funct[t.id] === "const") { + warning("E011", null, t.id); + } + if (funct["(global)"] && predefined[t.id] === false) { + warning("W079", t.token, t.id); + } + if (t.id) { + addlabel(t.id, "const"); + names.push(t.token); + } + } + if (prefix) { + break; + } + + this.first = this.first.concat(names); + + if (state.tokens.next.id !== "=") { + warning("E012", state.tokens.curr, state.tokens.curr.value); + } + + if (state.tokens.next.id === "=") { + nonadjacent(state.tokens.curr, state.tokens.next); + advance("="); + nonadjacent(state.tokens.curr, state.tokens.next); + if (state.tokens.next.id === "undefined") { + warning("W080", state.tokens.prev, state.tokens.prev.value); + } + if (peek(0).id === "=" && state.tokens.next.identifier) { + error("E037", state.tokens.next, state.tokens.next.value); + } + value = expression(5); + if (lone) { + tokens[0].first = value; + } else { + destructuringExpressionMatch(names, value); + } + } + + if (state.tokens.next.id !== ",") { + break; + } + comma(); + } + return this; + }); + conststatement.exps = true; var varstatement = stmt("var", function (prefix) { - var id, name, value; + var tokens, lone, value; - if (funct["(onevar)"] && option.onevar) { - warning("Too many var statements."); + if (funct["(onevar)"] && state.option.onevar) { + warning("W081"); } else if (!funct["(global)"]) { funct["(onevar)"] = true; } this.first = []; - for (;;) { - nonadjacent(token, nexttoken); - id = identifier(); - - if (option.esnext && funct[id] === "const") { - warning("const '" + id + "' has already been declared"); + var names = []; + nonadjacent(state.tokens.curr, state.tokens.next); + if (_.contains(["{", "["], state.tokens.next.value)) { + tokens = destructuringExpression(); + lone = false; + } else { + tokens = [ { id: identifier(), token: state.tokens.curr } ]; + lone = true; } - - if (funct["(global)"] && predefined[id] === false) { - warning("Redefinition of '{a}'.", token, id); + for (var t in tokens) { + t = tokens[t]; + if (state.option.inESNext() && funct[t.id] === "const") { + warning("E011", null, t.id); + } + if (funct["(global)"] && predefined[t.id] === false) { + warning("W079", t.token, t.id); + } + if (t.id) { + addlabel(t.id, "unused", t.token); + names.push(t.token); + } } - - addlabel(id, "unused", token); - if (prefix) { break; } - name = token; - this.first.push(token); + this.first = this.first.concat(names); - if (nexttoken.id === "=") { - nonadjacent(token, nexttoken); + if (state.tokens.next.id === "=") { + nonadjacent(state.tokens.curr, state.tokens.next); advance("="); - nonadjacent(token, nexttoken); - if (nexttoken.id === "undefined") { - warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id); + nonadjacent(state.tokens.curr, state.tokens.next); + if (state.tokens.next.id === "undefined") { + warning("W080", state.tokens.prev, state.tokens.prev.value); } - if (peek(0).id === "=" && nexttoken.identifier) { - error("Variable {a} was not declared correctly.", - nexttoken, nexttoken.value); + if (peek(0).id === "=" && state.tokens.next.identifier) { + error("E038", state.tokens.next, state.tokens.next.value); + } + value = expression(5); + if (lone) { + tokens[0].first = value; + } else { + destructuringExpressionMatch(names, value); } - value = expression(0); - name.first = value; } - if (nexttoken.id !== ",") { + + if (state.tokens.next.id !== ",") { break; } comma(); @@ -5498,63 +6090,190 @@ klass: return this; }); varstatement.exps = true; + var letstatement = stmt("let", function (prefix) { + var tokens, lone, value, letblock; + + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "let"); + } + + if (state.tokens.next.value === "(") { + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.next, "let block"); + } + advance("("); + funct["(blockscope)"].stack(); + letblock = true; + } else if (funct["(nolet)"]) { + error("E048", state.tokens.curr); + } + + if (funct["(onevar)"] && state.option.onevar) { + warning("W081"); + } else if (!funct["(global)"]) { + funct["(onevar)"] = true; + } + + this.first = []; + for (;;) { + var names = []; + nonadjacent(state.tokens.curr, state.tokens.next); + if (_.contains(["{", "["], state.tokens.next.value)) { + tokens = destructuringExpression(); + lone = false; + } else { + tokens = [ { id: identifier(), token: state.tokens.curr.value } ]; + lone = true; + } + for (var t in tokens) { + t = tokens[t]; + if (state.option.inESNext() && funct[t.id] === "const") { + warning("E011", null, t.id); + } + if (funct["(global)"] && predefined[t.id] === false) { + warning("W079", t.token, t.id); + } + if (t.id && !funct["(nolet)"]) { + addlabel(t.id, "unused", t.token, true); + names.push(t.token); + } + } + if (prefix) { + break; + } + + this.first = this.first.concat(names); + + if (state.tokens.next.id === "=") { + nonadjacent(state.tokens.curr, state.tokens.next); + advance("="); + nonadjacent(state.tokens.curr, state.tokens.next); + if (state.tokens.next.id === "undefined") { + warning("W080", state.tokens.prev, state.tokens.prev.value); + } + if (peek(0).id === "=" && state.tokens.next.identifier) { + error("E037", state.tokens.next, state.tokens.next.value); + } + value = expression(5); + if (lone) { + tokens[0].first = value; + } else { + destructuringExpressionMatch(names, value); + } + } + + if (state.tokens.next.id !== ",") { + break; + } + comma(); + } + if (letblock) { + advance(")"); + block(true, true); + this.block = true; + funct["(blockscope)"].unstack(); + } + + return this; + }); + letstatement.exps = true; + + blockstmt("class", function () { + return classdef.call(this, true); + }); + + function classdef(stmt) { + if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "class"); + } + if (stmt) { + this.name = identifier(); + addlabel(this.name, "unused", state.tokens.curr); + } else if (state.tokens.next.identifier && state.tokens.next.value !== "extends") { + this.name = identifier(); + } + classtail(this); + return this; + } + + function classtail(c) { + var strictness = state.directive["use strict"]; + if (state.tokens.next.value === "extends") { + advance("extends"); + c.heritage = expression(10); + } + state.directive["use strict"] = true; + advance("{"); + c.body = state.syntax["{"].nud(true); + state.directive["use strict"] = strictness; + } blockstmt("function", function () { + var generator = false; + if (state.tokens.next.value === "*") { + advance("*"); + if (state.option.inESNext(true)) { + generator = true; + } else { + warning("W119", state.tokens.curr, "function*"); + } + } if (inblock) { - warning("Function declarations should not be placed in blocks. " + - "Use a function expression or move the statement to the top of " + - "the outer function.", token); + warning("W082", state.tokens.curr); } var i = identifier(); - if (option.esnext && funct[i] === "const") { - warning("const '" + i + "' has already been declared"); + if (funct[i] === "const") { + warning("E011", null, i); } - adjacent(token, nexttoken); - addlabel(i, "unction", token); + adjacent(state.tokens.curr, state.tokens.next); + addlabel(i, "unction", state.tokens.curr); - doFunction(i, { statement: true }); - if (nexttoken.id === "(" && nexttoken.line === token.line) { - error( -"Function declarations are not invocable. Wrap the whole function invocation in parens."); + doFunction(i, { statement: true }, generator); + if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) { + error("E039"); } return this; }); prefix("function", function () { - var i = optionalidentifier(); - if (i) { - adjacent(token, nexttoken); - } else { - nonadjacent(token, nexttoken); + var generator = false; + if (state.tokens.next.value === "*") { + if (!state.option.inESNext()) { + warning("W119", state.tokens.curr, "function*"); + } + advance("*"); + generator = true; } - doFunction(i); - if (!option.loopfunc && funct["(loopage)"]) { - warning("Don't make functions within a loop."); + var i = optionalidentifier(); + if (i || state.option.gcl) { + adjacent(state.tokens.curr, state.tokens.next); + } else { + nonadjacent(state.tokens.curr, state.tokens.next); + } + doFunction(i, undefined, generator); + if (!state.option.loopfunc && funct["(loopage)"]) { + warning("W083"); } return this; }); blockstmt("if", function () { - var t = nexttoken; + var t = state.tokens.next; increaseComplexityCount(); + state.condition = true; advance("("); nonadjacent(this, t); nospace(); - expression(20); - if (nexttoken.id === "=") { - if (!option.boss) - warning("Assignment in conditional expression"); - advance("="); - expression(20); - } + checkCondAssignment(expression(0)); advance(")", t); - nospace(prevtoken, token); + state.condition = false; + nospace(state.tokens.prev, state.tokens.curr); block(true, true); - if (nexttoken.id === "else") { - nonadjacent(token, nexttoken); + if (state.tokens.next.id === "else") { + nonadjacent(state.tokens.curr, state.tokens.next); advance("else"); - if (nexttoken.id === "if" || nexttoken.id === "switch") { + if (state.tokens.next.id === "if" || state.tokens.next.id === "switch") { statement(true); } else { block(true, true); @@ -5571,87 +6290,96 @@ klass: var e; advance("catch"); - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.curr, state.tokens.next); advance("("); scope = Object.create(oldScope); - e = nexttoken.value; - if (nexttoken.type !== "(identifier)") { + e = state.tokens.next.value; + if (state.tokens.next.type !== "(identifier)") { e = null; - warning("Expected an identifier and instead saw '{a}'.", nexttoken, e); + warning("E030", state.tokens.next, e); } advance(); - advance(")"); funct = { - "(name)" : "(catch)", - "(line)" : nexttoken.line, - "(character)": nexttoken.character, + "(name)" : "(catch)", + "(line)" : state.tokens.next.line, + "(character)": state.tokens.next.character, "(context)" : funct, "(breakage)" : funct["(breakage)"], "(loopage)" : funct["(loopage)"], - "(scope)" : scope, + "(scope)" : scope, "(statement)": false, - "(metrics)" : createMetrics(nexttoken), - "(catch)" : true, - "(tokens)" : {} + "(metrics)" : createMetrics(state.tokens.next), + "(catch)" : true, + "(tokens)" : {}, + "(blockscope)": funct["(blockscope)"], + "(comparray)": funct["(comparray)"] }; if (e) { addlabel(e, "exception"); } - token.funct = funct; + if (state.tokens.next.value === "if") { + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.curr, "catch filter"); + } + advance("if"); + expression(0); + } + + advance(")"); + + state.tokens.curr.funct = funct; functions.push(funct); block(false); scope = oldScope; - funct["(last)"] = token.line; - funct["(lastcharacter)"] = token.character; + funct["(last)"] = state.tokens.curr.line; + funct["(lastcharacter)"] = state.tokens.curr.character; funct = funct["(context)"]; } block(false); - if (nexttoken.id === "catch") { + while (state.tokens.next.id === "catch") { increaseComplexityCount(); + if (b && (!state.option.inMoz(true))) { + warning("W118", state.tokens.next, "multiple catch blocks"); + } doCatch(); b = true; } - if (nexttoken.id === "finally") { + if (state.tokens.next.id === "finally") { advance("finally"); block(false); return; - } else if (!b) { - error("Expected '{a}' and instead saw '{b}'.", - nexttoken, "catch", nexttoken.value); + } + + if (!b) { + error("E021", state.tokens.next, "catch", state.tokens.next.value); } return this; }); blockstmt("while", function () { - var t = nexttoken; + var t = state.tokens.next; funct["(breakage)"] += 1; funct["(loopage)"] += 1; increaseComplexityCount(); advance("("); nonadjacent(this, t); nospace(); - expression(20); - if (nexttoken.id === "=") { - if (!option.boss) - warning("Assignment in conditional expression"); - advance("="); - expression(20); - } + checkCondAssignment(expression(0)); advance(")", t); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); block(true, true); funct["(breakage)"] -= 1; funct["(loopage)"] -= 1; @@ -5659,11 +6387,11 @@ klass: }).labelled = true; blockstmt("with", function () { - var t = nexttoken; - if (directive["use strict"]) { - error("'with' is not allowed in strict mode.", token); - } else if (!option.withstmt) { - warning("Don't use 'with'.", token); + var t = state.tokens.next; + if (state.directive["use strict"]) { + error("E010", state.tokens.curr); + } else if (!state.option.withstmt) { + warning("W085", state.tokens.curr); } advance("("); @@ -5671,32 +6399,34 @@ klass: nospace(); expression(0); advance(")", t); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); block(true, true); return this; }); blockstmt("switch", function () { - var t = nexttoken, + var t = state.tokens.next, g = false; funct["(breakage)"] += 1; advance("("); nonadjacent(this, t); nospace(); - this.condition = expression(20); + checkCondAssignment(expression(0)); advance(")", t); - nospace(prevtoken, token); - nonadjacent(token, nexttoken); - t = nexttoken; + nospace(state.tokens.prev, state.tokens.curr); + nonadjacent(state.tokens.curr, state.tokens.next); + t = state.tokens.next; advance("{"); - nonadjacent(token, nexttoken); - indent += option.indent; + nonadjacent(state.tokens.curr, state.tokens.next); + indent += state.option.indent; this.cases = []; + for (;;) { - switch (nexttoken.id) { + switch (state.tokens.next.id) { case "case": switch (funct["(verb)"]) { + case "yield": case "break": case "case": case "continue": @@ -5705,13 +6435,11 @@ klass: case "throw": break; default: - if (!ft.test(lines[nexttoken.line - 2])) { - warning( - "Expected a 'break' statement before 'case'.", - token); + if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { + warning("W086", state.tokens.curr, "case"); } } - indentation(-option.indent); + indentation(-state.option.indent); advance("case"); this.cases.push(expression(20)); increaseComplexityCount(); @@ -5721,60 +6449,55 @@ klass: break; case "default": switch (funct["(verb)"]) { + case "yield": case "break": case "continue": case "return": case "throw": break; default: - if (!ft.test(lines[nexttoken.line - 2])) { - warning( - "Expected a 'break' statement before 'default'.", - token); + if (this.cases.length) { + if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) { + warning("W086", state.tokens.curr, "default"); + } } } - indentation(-option.indent); + indentation(-state.option.indent); advance("default"); g = true; advance(":"); break; case "}": - indent -= option.indent; + indent -= state.option.indent; indentation(); advance("}", t); - if (this.cases.length === 1 || this.condition.id === "true" || - this.condition.id === "false") { - if (!option.onecase) - warning("This 'switch' should be an 'if'.", this); - } funct["(breakage)"] -= 1; funct["(verb)"] = undefined; return; case "(end)": - error("Missing '{a}'.", nexttoken, "}"); + error("E023", state.tokens.next, "}"); return; default: if (g) { - switch (token.id) { + switch (state.tokens.curr.id) { case ",": - error("Each value should have its own case label."); + error("E040"); return; case ":": g = false; statements(); break; default: - error("Missing ':' on a case clause.", token); + error("E025", state.tokens.curr); return; } } else { - if (token.id === ":") { + if (state.tokens.curr.id === ":") { advance(":"); - error("Unexpected '{a}'.", token, ":"); + error("E024", state.tokens.curr, ":"); statements(); } else { - error("Expected '{a}' and instead saw '{b}'.", - nexttoken, "case", nexttoken.value); + error("E021", state.tokens.next, "case", state.tokens.next.value); return; } } @@ -5783,8 +6506,8 @@ klass: }).labelled = true; stmt("debugger", function () { - if (!option.debug) { - warning("All 'debugger' statements should be removed."); + if (!state.option.debug) { + warning("W087"); } return this; }).exps = true; @@ -5795,21 +6518,15 @@ klass: funct["(loopage)"] += 1; increaseComplexityCount(); - this.first = block(true); + this.first = block(true, true); advance("while"); - var t = nexttoken; - nonadjacent(token, t); + var t = state.tokens.next; + nonadjacent(state.tokens.curr, t); advance("("); nospace(); - expression(20); - if (nexttoken.id === "=") { - if (!option.boss) - warning("Assignment in conditional expression"); - advance("="); - expression(20); - } + checkCondAssignment(expression(0)); advance(")", t); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); funct["(breakage)"] -= 1; funct["(loopage)"] -= 1; return this; @@ -5819,110 +6536,140 @@ klass: }()); blockstmt("for", function () { - var s, t = nexttoken; + var s, t = state.tokens.next; + var letscope = false; + var foreachtok = null; + + if (t.value === "each") { + foreachtok = t; + advance("each"); + if (!state.option.inMoz(true)) { + warning("W118", state.tokens.curr, "for each"); + } + } + funct["(breakage)"] += 1; funct["(loopage)"] += 1; increaseComplexityCount(); advance("("); nonadjacent(this, t); nospace(); - if (peek(nexttoken.id === "var" ? 1 : 0).id === "in") { - if (nexttoken.id === "var") { + var nextop; // contains the token of the "in" or "of" operator + var i = 0; + var inof = ["in", "of"]; + do { + nextop = peek(i); + ++i; + } while (!_.contains(inof, nextop.value) && nextop.value !== ";" && + nextop.type !== "(end)"); + if (_.contains(inof, nextop.value)) { + if (!state.option.inESNext() && nextop.value === "of") { + error("W104", nextop, "for of"); + } + if (state.tokens.next.id === "var") { advance("var"); - varstatement.fud.call(varstatement, true); + state.syntax["var"].fud.call(state.syntax["var"].fud, true); + } else if (state.tokens.next.id === "let") { + advance("let"); + letscope = true; + funct["(blockscope)"].stack(); + state.syntax["let"].fud.call(state.syntax["let"].fud, true); } else { - switch (funct[nexttoken.value]) { + switch (funct[state.tokens.next.value]) { case "unused": - funct[nexttoken.value] = "var"; + funct[state.tokens.next.value] = "var"; break; case "var": break; default: - warning("Bad for in variable '{a}'.", - nexttoken, nexttoken.value); + if (!funct["(blockscope)"].getlabel(state.tokens.next.value)) + warning("W088", state.tokens.next, state.tokens.next.value); } advance(); } - advance("in"); + advance(nextop.value); expression(20); advance(")", t); s = block(true, true); - if (option.forin && s && (s.length > 1 || typeof s[0] !== "object" || + if (state.option.forin && s && (s.length > 1 || typeof s[0] !== "object" || s[0].value !== "if")) { - warning("The body of a for in should be wrapped in an if statement to filter " + - "unwanted properties from the prototype.", this); + warning("W089", this); } funct["(breakage)"] -= 1; funct["(loopage)"] -= 1; - return this; } else { - if (nexttoken.id !== ";") { - if (nexttoken.id === "var") { + if (foreachtok) { + error("E045", foreachtok); + } + if (state.tokens.next.id !== ";") { + if (state.tokens.next.id === "var") { advance("var"); - varstatement.fud.call(varstatement); + state.syntax["var"].fud.call(state.syntax["var"].fud); + } else if (state.tokens.next.id === "let") { + advance("let"); + letscope = true; + funct["(blockscope)"].stack(); + state.syntax["let"].fud.call(state.syntax["let"].fud); } else { for (;;) { expression(0, "for"); - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } comma(); } } } - nolinebreak(token); + nolinebreak(state.tokens.curr); advance(";"); - if (nexttoken.id !== ";") { - expression(20); - if (nexttoken.id === "=") { - if (!option.boss) - warning("Assignment in conditional expression"); - advance("="); - expression(20); - } + if (state.tokens.next.id !== ";") { + checkCondAssignment(expression(0)); } - nolinebreak(token); + nolinebreak(state.tokens.curr); advance(";"); - if (nexttoken.id === ";") { - error("Expected '{a}' and instead saw '{b}'.", - nexttoken, ")", ";"); + if (state.tokens.next.id === ";") { + error("E021", state.tokens.next, ")", ";"); } - if (nexttoken.id !== ")") { + if (state.tokens.next.id !== ")") { for (;;) { expression(0, "for"); - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } comma(); } } advance(")", t); - nospace(prevtoken, token); + nospace(state.tokens.prev, state.tokens.curr); block(true, true); funct["(breakage)"] -= 1; funct["(loopage)"] -= 1; - return this; + } + if (letscope) { + funct["(blockscope)"].unstack(); + } + return this; }).labelled = true; stmt("break", function () { - var v = nexttoken.value; + var v = state.tokens.next.value; if (funct["(breakage)"] === 0) - warning("Unexpected '{a}'.", nexttoken, this.value); + warning("W052", state.tokens.next, this.value); - if (!option.asi) + if (!state.option.asi) nolinebreak(this); - if (nexttoken.id !== ";") { - if (token.line === nexttoken.line) { + if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { + if (state.tokens.curr.line === state.tokens.next.line) { if (funct[v] !== "label") { - warning("'{a}' is not a statement label.", nexttoken, v); + warning("W090", state.tokens.next, v); } else if (scope[v] !== funct) { - warning("'{a}' is out of scope.", nexttoken, v); + warning("W091", state.tokens.next, v); } - this.first = nexttoken; + this.first = state.tokens.next; advance(); } } @@ -5932,26 +6679,26 @@ klass: stmt("continue", function () { - var v = nexttoken.value; + var v = state.tokens.next.value; if (funct["(breakage)"] === 0) - warning("Unexpected '{a}'.", nexttoken, this.value); + warning("W052", state.tokens.next, this.value); - if (!option.asi) + if (!state.option.asi) nolinebreak(this); - if (nexttoken.id !== ";") { - if (token.line === nexttoken.line) { + if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { + if (state.tokens.curr.line === state.tokens.next.line) { if (funct[v] !== "label") { - warning("'{a}' is not a statement label.", nexttoken, v); + warning("W090", state.tokens.next, v); } else if (scope[v] !== funct) { - warning("'{a}' is out of scope.", nexttoken, v); + warning("W091", state.tokens.next, v); } - this.first = nexttoken; + this.first = state.tokens.next; advance(); } } else if (!funct["(loopage)"]) { - warning("Unexpected '{a}'.", nexttoken, this.value); + warning("W052", state.tokens.next, this.value); } reachable("continue"); return this; @@ -5959,86 +6706,248 @@ klass: stmt("return", function () { - if (this.line === nexttoken.line) { - if (nexttoken.id === "(regexp)") - warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator."); + if (this.line === state.tokens.next.line) { + if (state.tokens.next.id === "(regexp)") + warning("W092"); - if (nexttoken.id !== ";" && !nexttoken.reach) { - nonadjacent(token, nexttoken); - if (peek().value === "=" && !option.boss) { - warningAt("Did you mean to return a conditional instead of an assignment?", - token.line, token.character + 1); - } + if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { + nonadjacent(state.tokens.curr, state.tokens.next); this.first = expression(0); + + if (this.first && + this.first.type === "(punctuator)" && this.first.value === "=" && !state.option.boss) { + warningAt("W093", this.first.line, this.first.character); + } + } + } else { + if (state.tokens.next.type === "(punctuator)" && + ["[", "{", "+", "-"].indexOf(state.tokens.next.value) > -1) { + nolinebreak(this); // always warn (Line breaking error) } - } else if (!option.asi) { - nolinebreak(this); // always warn (Line breaking error) } reachable("return"); return this; }).exps = true; + stmt("yield", function () { + if (state.option.inESNext(true) && funct["(generator)"] !== true) { + error("E046", state.tokens.curr, "yield"); + } else if (!state.option.inESNext()) { + warning("W104", state.tokens.curr, "yield"); + } + funct["(generator)"] = "yielded"; + if (this.line === state.tokens.next.line) { + if (state.tokens.next.id === "(regexp)") + warning("W092"); + + if (state.tokens.next.id !== ";" && !state.tokens.next.reach) { + nonadjacent(state.tokens.curr, state.tokens.next); + this.first = expression(0); + + if (this.first.type === "(punctuator)" && this.first.value === "=" && !state.option.boss) { + warningAt("W093", this.first.line, this.first.character); + } + } + } else if (!state.option.asi) { + nolinebreak(this); // always warn (Line breaking error) + } + return this; + }).exps = true; + stmt("throw", function () { nolinebreak(this); - nonadjacent(token, nexttoken); + nonadjacent(state.tokens.curr, state.tokens.next); this.first = expression(20); reachable("throw"); return this; }).exps = true; - reserve("class"); - reserve("const"); - reserve("enum"); - reserve("export"); - reserve("extends"); - reserve("import"); - reserve("super"); + FutureReservedWord("abstract"); + FutureReservedWord("boolean"); + FutureReservedWord("byte"); + FutureReservedWord("char"); + FutureReservedWord("class", { es5: true, nud: classdef }); + FutureReservedWord("double"); + FutureReservedWord("enum", { es5: true }); + FutureReservedWord("export", { es5: true }); + FutureReservedWord("extends", { es5: true }); + FutureReservedWord("final"); + FutureReservedWord("float"); + FutureReservedWord("goto"); + FutureReservedWord("implements", { es5: true, strictOnly: true }); + FutureReservedWord("import", { es5: true }); + FutureReservedWord("int"); + FutureReservedWord("interface", { es5: true, strictOnly: true }); + FutureReservedWord("long"); + FutureReservedWord("native"); + FutureReservedWord("package", { es5: true, strictOnly: true }); + FutureReservedWord("private", { es5: true, strictOnly: true }); + FutureReservedWord("protected", { es5: true, strictOnly: true }); + FutureReservedWord("public", { es5: true, strictOnly: true }); + FutureReservedWord("short"); + FutureReservedWord("static", { es5: true, strictOnly: true }); + FutureReservedWord("super", { es5: true }); + FutureReservedWord("synchronized"); + FutureReservedWord("throws"); + FutureReservedWord("transient"); + FutureReservedWord("volatile"); - reserve("let"); - reserve("yield"); - reserve("implements"); - reserve("interface"); - reserve("package"); - reserve("private"); - reserve("protected"); - reserve("public"); - reserve("static"); + var lookupBlockType = function () { + var pn, pn1; + var i = 0; + var bracketStack = 0; + var ret = {}; + if (_.contains(["[", "{"], state.tokens.curr.value)) + bracketStack += 1; + if (_.contains(["[", "{"], state.tokens.next.value)) + bracketStack += 1; + if (_.contains(["]", "}"], state.tokens.next.value)) + bracketStack -= 1; + do { + pn = peek(i); + pn1 = peek(i + 1); + i = i + 1; + if (_.contains(["[", "{"], pn.value)) { + bracketStack += 1; + } else if (_.contains(["]", "}"], pn.value)) { + bracketStack -= 1; + } + if (pn.identifier && pn.value === "for" && bracketStack === 1) { + ret.isCompArray = true; + ret.notJson = true; + break; + } + if (_.contains(["}", "]"], pn.value) && pn1.value === "=") { + ret.isDestAssign = true; + ret.notJson = true; + break; + } + if (pn.value === ";") { + ret.isBlock = true; + ret.notJson = true; + } + } while (bracketStack > 0 && pn.id !== "(end)" && i < 15); + return ret; + }; + function destructuringAssignOrJsonValue() { + + var block = lookupBlockType(); + if (block.notJson) { + if (!state.option.inESNext() && block.isDestAssign) { + warning("W104", state.tokens.curr, "destructuring assignment"); + } + statements(); + } else { + state.option.laxbreak = true; + state.jsonMode = true; + jsonValue(); + } + } + + var arrayComprehension = function () { + var CompArray = function () { + this.mode = "use"; + this.variables = []; + }; + var _carrays = []; + var _current; + function declare(v) { + var l = _current.variables.filter(function (elt) { + if (elt.value === v) { + elt.undef = false; + return v; + } + }).length; + return l !== 0; + } + function use(v) { + var l = _current.variables.filter(function (elt) { + if (elt.value === v && !elt.undef) { + if (elt.unused === true) { + elt.unused = false; + } + return v; + } + }).length; + return (l === 0); + } + return {stack: function () { + _current = new CompArray(); + _carrays.push(_current); + }, + unstack: function () { + _current.variables.filter(function (v) { + if (v.unused) + warning("W098", v.token, v.value); + if (v.undef) + isundef(v.funct, "W117", v.token, v.value); + }); + _carrays.splice(_carrays[_carrays.length - 1], 1); + _current = _carrays[_carrays.length - 1]; + }, + setState: function (s) { + if (_.contains(["use", "define", "filter"], s)) + _current.mode = s; + }, + check: function (v) { + if (_current && _current.mode === "use") { + _current.variables.push({funct: funct, + token: state.tokens.curr, + value: v, + undef: true, + unused: false}); + return true; + } else if (_current && _current.mode === "define") { + if (!declare(v)) { + _current.variables.push({funct: funct, + token: state.tokens.curr, + value: v, + undef: false, + unused: true}); + } + return true; + } else if (_current && _current.mode === "filter") { + if (use(v)) { + isundef(funct, "W117", state.tokens.curr, v); + } + return true; + } + return false; + } + }; + }; function jsonValue() { function jsonObject() { - var o = {}, t = nexttoken; + var o = {}, t = state.tokens.next; advance("{"); - if (nexttoken.id !== "}") { + if (state.tokens.next.id !== "}") { for (;;) { - if (nexttoken.id === "(end)") { - error("Missing '}' to match '{' from line {a}.", - nexttoken, t.line); - } else if (nexttoken.id === "}") { - warning("Unexpected comma.", token); + if (state.tokens.next.id === "(end)") { + error("E026", state.tokens.next, t.line); + } else if (state.tokens.next.id === "}") { + warning("W094", state.tokens.curr); break; - } else if (nexttoken.id === ",") { - error("Unexpected comma.", nexttoken); - } else if (nexttoken.id !== "(string)") { - warning("Expected a string and instead saw {a}.", - nexttoken, nexttoken.value); + } else if (state.tokens.next.id === ",") { + error("E028", state.tokens.next); + } else if (state.tokens.next.id !== "(string)") { + warning("W095", state.tokens.next, state.tokens.next.value); } - if (o[nexttoken.value] === true) { - warning("Duplicate key '{a}'.", - nexttoken, nexttoken.value); - } else if ((nexttoken.value === "__proto__" && - !option.proto) || (nexttoken.value === "__iterator__" && - !option.iterator)) { - warning("The '{a}' key may produce unexpected results.", - nexttoken, nexttoken.value); + if (o[state.tokens.next.value] === true) { + warning("W075", state.tokens.next, state.tokens.next.value); + } else if ((state.tokens.next.value === "__proto__" && + !state.option.proto) || (state.tokens.next.value === "__iterator__" && + !state.option.iterator)) { + warning("W096", state.tokens.next, state.tokens.next.value); } else { - o[nexttoken.value] = true; + o[state.tokens.next.value] = true; } advance(); advance(":"); jsonValue(); - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } advance(","); @@ -6048,21 +6957,20 @@ klass: } function jsonArray() { - var t = nexttoken; + var t = state.tokens.next; advance("["); - if (nexttoken.id !== "]") { + if (state.tokens.next.id !== "]") { for (;;) { - if (nexttoken.id === "(end)") { - error("Missing ']' to match '[' from line {a}.", - nexttoken, t.line); - } else if (nexttoken.id === "]") { - warning("Unexpected comma.", token); + if (state.tokens.next.id === "(end)") { + error("E027", state.tokens.next, t.line); + } else if (state.tokens.next.id === "]") { + warning("W094", state.tokens.curr); break; - } else if (nexttoken.id === ",") { - error("Unexpected comma.", nexttoken); + } else if (state.tokens.next.id === ",") { + error("E028", state.tokens.next); } jsonValue(); - if (nexttoken.id !== ",") { + if (state.tokens.next.id !== ",") { break; } advance(","); @@ -6071,7 +6979,7 @@ klass: advance("]"); } - switch (nexttoken.id) { + switch (state.tokens.next.id) { case "{": jsonObject(); break; @@ -6087,20 +6995,72 @@ klass: break; case "-": advance("-"); - if (token.character !== nexttoken.from) { - warning("Unexpected space after '-'.", token); + if (state.tokens.curr.character !== state.tokens.next.from) { + warning("W011", state.tokens.curr); } - adjacent(token, nexttoken); + adjacent(state.tokens.curr, state.tokens.next); advance("(number)"); break; default: - error("Expected a JSON value.", nexttoken); + error("E003", state.tokens.next); } } + + var blockScope = function () { + var _current = {}; + var _variables = [_current]; + + function _checkBlockLabels() { + for (var t in _current) { + if (_current[t]["(type)"] === "unused") { + if (state.option.unused) { + var tkn = _current[t]["(token)"]; + var line = tkn.line; + var chr = tkn.character; + warningAt("W098", line, chr, t); + } + } + } + } + + return { + stack: function () { + _current = {}; + _variables.push(_current); + }, + + unstack: function () { + _checkBlockLabels(); + _variables.splice(_variables.length - 1, 1); + _current = _.last(_variables); + }, + + getlabel: function (l) { + for (var i = _variables.length - 1 ; i >= 0; --i) { + if (_.has(_variables[i], l)) { + return _variables[i]; + } + } + }, + + current: { + has: function (t) { + return _.has(_current, t); + }, + add: function (t, type, tok) { + _current[t] = { "(type)" : type, + "(token)": tok }; + } + } + }; + }; var itself = function (s, o, g) { - var a, i, k, x; + var i, k, x; var optionKeys; var newOptionObj = {}; + var newIgnoredObj = {}; + + state.reset(); if (o && o.scope) { JSHINT.scope = o.scope; @@ -6112,48 +7072,67 @@ klass: JSHINT.scope = "(main)"; } - predefined = Object.create(standard); - declared = Object.create(null); + predefined = Object.create(null); + combine(predefined, vars.ecmaIdentifiers); + combine(predefined, vars.reservedVars); + combine(predefined, g || {}); + declared = Object.create(null); + exported = Object.create(null); + + function each(obj, cb) { + if (!obj) + return; + + if (!Array.isArray(obj) && typeof obj === "object") + obj = Object.keys(obj); + + obj.forEach(cb); + } + if (o) { - a = o.predef; - if (a) { - if (!Array.isArray(a) && typeof a === "object") { - a = Object.keys(a); + each(o.predef || null, function (item) { + var slice, prop; + + if (item[0] === "-") { + slice = item.slice(1); + JSHINT.blacklist[slice] = slice; + } else { + prop = Object.getOwnPropertyDescriptor(o.predef, item); + predefined[item] = prop ? prop.value : false; } - a.forEach(function (item) { - var slice; - if (item[0] === "-") { - slice = item.slice(1); - JSHINT.blacklist[slice] = slice; - } else { - predefined[item] = true; - } - }); - } + }); + + each(o.exported || null, function (item) { + exported[item] = true; + }); + + delete o.predef; + delete o.exported; optionKeys = Object.keys(o); for (x = 0; x < optionKeys.length; x++) { - newOptionObj[optionKeys[x]] = o[optionKeys[x]]; + if (/^-W\d{3}$/g.test(optionKeys[x])) { + newIgnoredObj[optionKeys[x].slice(1)] = true; + } else { + newOptionObj[optionKeys[x]] = o[optionKeys[x]]; - if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false) - newOptionObj["(explicitNewcap)"] = true; + if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false) + newOptionObj["(explicitNewcap)"] = true; - if (optionKeys[x] === "indent") - newOptionObj.white = true; + if (optionKeys[x] === "indent") + newOptionObj["(explicitIndent)"] = o[optionKeys[x]] === false ? false : true; + } } } - option = newOptionObj; + state.option = newOptionObj; + state.ignored = newIgnoredObj; - option.indent = option.indent || 4; - option.maxerr = option.maxerr || 50; + state.option.indent = state.option.indent || 4; + state.option.maxerr = state.option.maxerr || 50; - tab = ""; - for (i = 0; i < option.indent; i += 1) { - tab += " "; - } indent = 1; global = Object.create(predefined); scope = global; @@ -6164,7 +7143,9 @@ klass: "(breakage)": 0, "(loopage)": 0, "(tokens)": {}, - "(metrics)": createMetrics(nexttoken) + "(metrics)": createMetrics(state.tokens.next), + "(blockscope)": blockScope(), + "(comparray)": arrayComprehension() }; functions = [funct]; urls = []; @@ -6174,61 +7155,106 @@ klass: implied = {}; inblock = false; lookahead = []; - jsonmode = false; warnings = 0; - lines = []; unuseds = []; if (!isString(s) && !Array.isArray(s)) { - errorAt("Input is neither a string nor an array of strings.", 0); + errorAt("E004", 0); return false; } - if (isString(s) && /^\s*$/g.test(s)) { - errorAt("Input is an empty string.", 0); - return false; - } + api = { + get isJSON() { + return state.jsonMode; + }, - if (s.length === 0) { - errorAt("Input is an empty array.", 0); - return false; - } + getOption: function (name) { + return state.option[name] || null; + }, - lex.init(s); + getCache: function (name) { + return state.cache[name]; + }, - prereg = true; - directive = {}; + setCache: function (name, value) { + state.cache[name] = value; + }, - prevtoken = token = nexttoken = syntax["(begin)"]; + warn: function (code, data) { + warningAt.apply(null, [ code, data.line, data.char ].concat(data.data)); + }, + + on: function (names, listener) { + names.split(" ").forEach(function (name) { + emitter.on(name, listener); + }.bind(this)); + } + }; + + emitter.removeAllListeners(); + (extraModules || []).forEach(function (func) { + func(api); + }); + + state.tokens.prev = state.tokens.curr = state.tokens.next = state.syntax["(begin)"]; + + lex = new Lexer(s); + + lex.on("warning", function (ev) { + warningAt.apply(null, [ ev.code, ev.line, ev.character].concat(ev.data)); + }); + + lex.on("error", function (ev) { + errorAt.apply(null, [ ev.code, ev.line, ev.character ].concat(ev.data)); + }); + + lex.on("fatal", function (ev) { + quit("E041", ev.line, ev.from); + }); + + lex.on("Identifier", function (ev) { + emitter.emit("Identifier", ev); + }); + + lex.on("String", function (ev) { + emitter.emit("String", ev); + }); + + lex.on("Number", function (ev) { + emitter.emit("Number", ev); + }); + + lex.start(); for (var name in o) { - if (is_own(o, name)) { - checkOption(name, token); + if (_.has(o, name)) { + checkOption(name, state.tokens.curr); } } assume(); combine(predefined, g || {}); comma.first = true; - quotmark = undefined; try { advance(); - switch (nexttoken.id) { + switch (state.tokens.next.id) { case "{": case "[": - option.laxbreak = true; - jsonmode = true; - jsonValue(); + destructuringAssignOrJsonValue(); break; default: directives(); - if (directive["use strict"] && !option.globalstrict) { - warning("Use the function form of \"use strict\".", prevtoken); + + if (state.directive["use strict"]) { + if (!state.option.globalstrict && !state.option.node) { + warning("W097", state.tokens.prev); + } } statements(); } - advance((nexttoken && nexttoken.value !== ".") ? "(end)" : undefined); + advance((state.tokens.next && state.tokens.next.value !== ".") ? "(end)" : undefined); + funct["(blockscope)"].unstack(); var markDefined = function (name, context) { do { @@ -6264,12 +7290,29 @@ klass: implied[name] = newImplied; }; - var warnUnused = function (name, token) { - var line = token.line; - var chr = token.character; + var warnUnused = function (name, tkn, type, unused_opt) { + var line = tkn.line; + var chr = tkn.character; - if (option.unused) - warningAt("'{a}' is defined but never used.", line, chr, name); + if (unused_opt === undefined) { + unused_opt = state.option.unused; + } + + if (unused_opt === true) { + unused_opt = "last-param"; + } + + var warnable_types = { + "vars": ["var"], + "last-param": ["var", "param"], + "strict": ["var", "param", "last-param"] + }; + + if (unused_opt) { + if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) { + warningAt("W098", line, chr, name); + } + } unuseds.push({ name: name, @@ -6280,7 +7323,7 @@ klass: var checkUnused = function (func, key) { var type = func[key]; - var token = func["(tokens)"][key]; + var tkn = func["(tokens)"][key]; if (key.charAt(0) === "(") return; @@ -6289,22 +7332,29 @@ klass: return; if (func["(params)"] && func["(params)"].indexOf(key) !== -1) return; + if (func["(global)"] && _.has(exported, key)) { + return; + } - warnUnused(key, token); + warnUnused(key, tkn, "var"); }; for (i = 0; i < JSHINT.undefs.length; i += 1) { k = JSHINT.undefs[i].slice(0); if (markDefined(k[2].value, k[0])) { clearImplied(k[2].value, k[2].line); - } else { + } else if (state.option.undef) { warning.apply(warning, k.slice(1)); } } functions.forEach(function (func) { + if (func["(unusedOption)"] === false) { + return; + } + for (var key in func) { - if (is_own(func, key)) { + if (_.has(func, key)) { checkUnused(func, key); } } @@ -6314,36 +7364,44 @@ klass: var params = func["(params)"].slice(); var param = params.pop(); - var type; + var type, unused_opt; while (param) { type = func[param]; + unused_opt = func["(unusedOption)"] || state.option.unused; + unused_opt = unused_opt === true ? "last-param" : unused_opt; if (param === "undefined") return; - if (type !== "unused" && type !== "unction") + if (type === "unused" || type === "unction") { + warnUnused(param, func["(tokens)"][param], "param", func["(unusedOption)"]); + } else if (unused_opt === "last-param") { return; + } - warnUnused(param, func["(tokens)"][param]); param = params.pop(); } }); for (var key in declared) { - if (is_own(declared, key) && !is_own(global, key)) { - warnUnused(key, declared[key]); + if (_.has(declared, key) && !_.has(global, key)) { + warnUnused(key, declared[key], "var"); } } - } catch (e) { - if (e) { - var nt = nexttoken || {}; + + } catch (err) { + if (err && err.name === "JSHintError") { + var nt = state.tokens.next || {}; JSHINT.errors.push({ - raw : e.raw, - reason : e.message, - line : e.line || nt.line, - character : e.character || nt.from + scope : "(main)", + raw : err.raw, + reason : err.message, + line : err.line || nt.line, + character : err.character || nt.from }, null); + } else { + throw err; } } @@ -6359,10 +7417,15 @@ klass: return JSHINT.errors.length === 0; }; + itself.addModule = function (func) { + extraModules.push(func); + }; + + itself.addModule(style.register); itself.data = function () { var data = { functions: [], - options: option + options: state.option }; var implieds = []; var members = []; @@ -6372,12 +7435,12 @@ klass: data.errors = itself.errors; } - if (jsonmode) { + if (state.jsonMode) { data.json = true; } for (n in implied) { - if (is_own(implied, n)) { + if (_.has(implied, n)) { implieds.push({ name: n, line: implied[n] @@ -6444,4 +7507,2474 @@ if (typeof exports === "object" && exports) { exports.JSHINT = JSHINT; } +})() +}, +{"events":2,"../shared/vars.js":3,"../shared/messages.js":10,"./lex.js":11,"./reg.js":4,"./state.js":5,"./style.js":6,"console-browserify":7,"underscore":12}], +10:[function(req,module,exports){ +(function(){ + +var _ = req("underscore"); + +var errors = { + E001: "Bad option: '{a}'.", + E002: "Bad option value.", + E003: "Expected a JSON value.", + E004: "Input is neither a string nor an array of strings.", + E005: "Input is empty.", + E006: "Unexpected early end of program.", + E007: "Missing \"use strict\" statement.", + E008: "Strict violation.", + E009: "Option 'validthis' can't be used in a global scope.", + E010: "'with' is not allowed in strict mode.", + E011: "const '{a}' has already been declared.", + E012: "const '{a}' is initialized to 'undefined'.", + E013: "Attempting to override '{a}' which is a constant.", + E014: "A regular expression literal can be confused with '/='.", + E015: "Unclosed regular expression.", + E016: "Invalid regular expression.", + E017: "Unclosed comment.", + E018: "Unbegun comment.", + E019: "Unmatched '{a}'.", + E020: "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", + E021: "Expected '{a}' and instead saw '{b}'.", + E022: "Line breaking error '{a}'.", + E023: "Missing '{a}'.", + E024: "Unexpected '{a}'.", + E025: "Missing ':' on a case clause.", + E026: "Missing '}' to match '{' from line {a}.", + E027: "Missing ']' to match '[' form line {a}.", + E028: "Illegal comma.", + E029: "Unclosed string.", + E030: "Expected an identifier and instead saw '{a}'.", + E031: "Bad assignment.", // FIXME: Rephrase + E032: "Expected a small integer or 'false' and instead saw '{a}'.", + E033: "Expected an operator and instead saw '{a}'.", + E034: "get/set are ES5 features.", + E035: "Missing property name.", + E036: "Expected to see a statement and instead saw a block.", + E037: "Constant {a} was not declared correctly.", + E038: "Variable {a} was not declared correctly.", + E039: "Function declarations are not invocable. Wrap the whole function invocation in parens.", + E040: "Each value should have its own case label.", + E041: "Unrecoverable syntax error.", + E042: "Stopping.", + E043: "Too many errors.", + E044: "'{a}' is already defined and can't be redefined.", + E045: "Invalid for each loop.", + E046: "A yield statement shall be within a generator function (with syntax: `function*`)", + E047: "A generator function shall contain a yield statement.", + E048: "Let declaration not directly within block.", + E049: "A {a} cannot be named '{b}'." +}; + +var warnings = { + W001: "'hasOwnProperty' is a really bad name.", + W002: "Value of '{a}' may be overwritten in IE 8 and earlier.", + W003: "'{a}' was used before it was defined.", + W004: "'{a}' is already defined.", + W005: "A dot following a number can be confused with a decimal point.", + W006: "Confusing minuses.", + W007: "Confusing pluses.", + W008: "A leading decimal point can be confused with a dot: '{a}'.", + W009: "The array literal notation [] is preferrable.", + W010: "The object literal notation {} is preferrable.", + W011: "Unexpected space after '{a}'.", + W012: "Unexpected space before '{a}'.", + W013: "Missing space after '{a}'.", + W014: "Bad line breaking before '{a}'.", + W015: "Expected '{a}' to have an indentation at {b} instead at {c}.", + W016: "Unexpected use of '{a}'.", + W017: "Bad operand.", + W018: "Confusing use of '{a}'.", + W019: "Use the isNaN function to compare with NaN.", + W020: "Read only.", + W021: "'{a}' is a function.", + W022: "Do not assign to the exception parameter.", + W023: "Expected an identifier in an assignment and instead saw a function invocation.", + W024: "Expected an identifier and instead saw '{a}' (a reserved word).", + W025: "Missing name in function declaration.", + W026: "Inner functions should be listed at the top of the outer function.", + W027: "Unreachable '{a}' after '{b}'.", + W028: "Label '{a}' on {b} statement.", + W030: "Expected an assignment or function call and instead saw an expression.", + W031: "Do not use 'new' for side effects.", + W032: "Unnecessary semicolon.", + W033: "Missing semicolon.", + W034: "Unnecessary directive \"{a}\".", + W035: "Empty block.", + W036: "Unexpected /*member '{a}'.", + W037: "'{a}' is a statement label.", + W038: "'{a}' used out of scope.", + W039: "'{a}' is not allowed.", + W040: "Possible strict violation.", + W041: "Use '{a}' to compare with '{b}'.", + W042: "Avoid EOL escaping.", + W043: "Bad escaping of EOL. Use option multistr if needed.", + W044: "Bad or unnecessary escaping.", + W045: "Bad number '{a}'.", + W046: "Don't use extra leading zeros '{a}'.", + W047: "A trailing decimal point can be confused with a dot: '{a}'.", + W048: "Unexpected control character in regular expression.", + W049: "Unexpected escaped character '{a}' in regular expression.", + W050: "JavaScript URL.", + W051: "Variables should not be deleted.", + W052: "Unexpected '{a}'.", + W053: "Do not use {a} as a constructor.", + W054: "The Function constructor is a form of eval.", + W055: "A constructor name should start with an uppercase letter.", + W056: "Bad constructor.", + W057: "Weird construction. Is 'new' unnecessary?", + W058: "Missing '()' invoking a constructor.", + W059: "Avoid arguments.{a}.", + W060: "document.write can be a form of eval.", + W061: "eval can be harmful.", + W062: "Wrap an immediate function invocation in parens " + + "to assist the reader in understanding that the expression " + + "is the result of a function, and not the function itself.", + W063: "Math is not a function.", + W064: "Missing 'new' prefix when invoking a constructor.", + W065: "Missing radix parameter.", + W066: "Implied eval. Consider passing a function instead of a string.", + W067: "Bad invocation.", + W068: "Wrapping non-IIFE function literals in parens is unnecessary.", + W069: "['{a}'] is better written in dot notation.", + W070: "Extra comma. (it breaks older versions of IE)", + W071: "This function has too many statements. ({a})", + W072: "This function has too many parameters. ({a})", + W073: "Blocks are nested too deeply. ({a})", + W074: "This function's cyclomatic complexity is too high. ({a})", + W075: "Duplicate key '{a}'.", + W076: "Unexpected parameter '{a}' in get {b} function.", + W077: "Expected a single parameter in set {a} function.", + W078: "Setter is defined without getter.", + W079: "Redefinition of '{a}'.", + W080: "It's not necessary to initialize '{a}' to 'undefined'.", + W081: "Too many var statements.", + W082: "Function declarations should not be placed in blocks. " + + "Use a function expression or move the statement to the top of " + + "the outer function.", + W083: "Don't make functions within a loop.", + W084: "Assignment in conditional expression", + W085: "Don't use 'with'.", + W086: "Expected a 'break' statement before '{a}'.", + W087: "Forgotten 'debugger' statement?", + W088: "Creating global 'for' variable. Should be 'for (var {a} ...'.", + W089: "The body of a for in should be wrapped in an if statement to filter " + + "unwanted properties from the prototype.", + W090: "'{a}' is not a statement label.", + W091: "'{a}' is out of scope.", + W092: "Wrap the /regexp/ literal in parens to disambiguate the slash operator.", + W093: "Did you mean to return a conditional instead of an assignment?", + W094: "Unexpected comma.", + W095: "Expected a string and instead saw {a}.", + W096: "The '{a}' key may produce unexpected results.", + W097: "Use the function form of \"use strict\".", + W098: "'{a}' is defined but never used.", + W099: "Mixed spaces and tabs.", + W100: "This character may get silently deleted by one or more browsers.", + W101: "Line is too long.", + W102: "Trailing whitespace.", + W103: "The '{a}' property is deprecated.", + W104: "'{a}' is only available in JavaScript 1.7.", + W105: "Unexpected {a} in '{b}'.", + W106: "Identifier '{a}' is not in camel case.", + W107: "Script URL.", + W108: "Strings must use doublequote.", + W109: "Strings must use singlequote.", + W110: "Mixed double and single quotes.", + W112: "Unclosed string.", + W113: "Control character in string: {a}.", + W114: "Avoid {a}.", + W115: "Octal literals are not allowed in strict mode.", + W116: "Expected '{a}' and instead saw '{b}'.", + W117: "'{a}' is not defined.", + W118: "'{a}' is only available in Mozilla JavaScript extensions (use moz option).", + W119: "'{a}' is only available in ES6 (use esnext option)." +}; + +var info = { + I001: "Comma warnings can be turned off with 'laxcomma'.", + I002: "Reserved words as properties can be used under the 'es5' option.", + I003: "ES5 option is now set per default" +}; + +exports.errors = {}; +exports.warnings = {}; +exports.info = {}; + +_.each(errors, function (desc, code) { + exports.errors[code] = { code: code, desc: desc }; +}); + +_.each(warnings, function (desc, code) { + exports.warnings[code] = { code: code, desc: desc }; +}); + +_.each(info, function (desc, code) { + exports.info[code] = { code: code, desc: desc }; +}); + +})() +}, +{"underscore":12}], +11:[function(req,module,exports){ +(function(){/* + * Lexical analysis and token construction. + */ + + + +var _ = req("underscore"); +var events = req("events"); +var reg = req("./reg.js"); +var state = req("./state.js").state; + +var Token = { + Identifier: 1, + Punctuator: 2, + NumericLiteral: 3, + StringLiteral: 4, + Comment: 5, + Keyword: 6, + NullLiteral: 7, + BooleanLiteral: 8, + RegExp: 9 +}; + +var unicodeLetterTable = [ + 170, 170, 181, 181, 186, 186, 192, 214, + 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, + 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908, + 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366, + 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610, + 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, + 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, + 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, + 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2308, 2361, + 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431, + 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, + 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, + 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, + 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, + 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, + 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, + 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, + 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, + 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, + 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, + 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, + 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212, + 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, + 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344, + 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455, + 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, + 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, + 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, + 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, + 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805, + 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, + 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, + 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4304, 4346, + 4348, 4348, 4352, 4680, 4682, 4685, 4688, 4694, 4696, 4696, + 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, + 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, + 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740, + 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, + 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, + 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312, + 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516, + 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823, + 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7104, 7141, + 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409, + 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, + 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, + 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, + 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, + 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, + 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, + 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, + 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, + 11360, 11492, 11499, 11502, 11520, 11557, 11568, 11621, + 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, + 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, + 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295, + 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, + 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, + 12593, 12686, 12704, 12730, 12784, 12799, 13312, 13312, + 19893, 19893, 19968, 19968, 40907, 40907, 40960, 42124, + 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, + 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783, + 42786, 42888, 42891, 42894, 42896, 42897, 42912, 42921, + 43002, 43009, 43011, 43013, 43015, 43018, 43020, 43042, + 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, + 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, + 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595, + 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697, + 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, + 43739, 43741, 43777, 43782, 43785, 43790, 43793, 43798, + 43808, 43814, 43816, 43822, 43968, 44002, 44032, 44032, + 55203, 55203, 55216, 55238, 55243, 55291, 63744, 64045, + 64048, 64109, 64112, 64217, 64256, 64262, 64275, 64279, + 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, + 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, + 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, + 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, + 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, + 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, + 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, + 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66334, + 66352, 66378, 66432, 66461, 66464, 66499, 66504, 66511, + 66513, 66517, 66560, 66717, 67584, 67589, 67592, 67592, + 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, + 67840, 67861, 67872, 67897, 68096, 68096, 68112, 68115, + 68117, 68119, 68121, 68147, 68192, 68220, 68352, 68405, + 68416, 68437, 68448, 68466, 68608, 68680, 69635, 69687, + 69763, 69807, 73728, 74606, 74752, 74850, 77824, 78894, + 92160, 92728, 110592, 110593, 119808, 119892, 119894, 119964, + 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, + 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, + 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, + 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, + 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, + 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, + 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, + 131072, 131072, 173782, 173782, 173824, 173824, 177972, 177972, + 177984, 177984, 178205, 178205, 194560, 195101 +]; + +var identifierStartTable = []; + +for (var i = 0; i < 128; i++) { + identifierStartTable[i] = + i === 36 || // $ + i >= 65 && i <= 90 || // A-Z + i === 95 || // _ + i >= 97 && i <= 122; // a-z +} + +var identifierPartTable = []; + +for (var i = 0; i < 128; i++) { + identifierPartTable[i] = + identifierStartTable[i] || // $, _, A-Z, a-z + i >= 48 && i <= 57; // 0-9 +} + +function asyncTrigger() { + var _checks = []; + + return { + push: function (fn) { + _checks.push(fn); + }, + + check: function () { + for (var check = 0; check < _checks.length; ++check) { + _checks[check](); + } + + _checks.splice(0, _checks.length); + } + }; +} +function Lexer(source) { + var lines = source; + + if (typeof lines === "string") { + lines = lines + .replace(/\r\n/g, "\n") + .replace(/\r/g, "\n") + .split("\n"); + } + + if (lines[0] && lines[0].substr(0, 2) === "#!") { + lines[0] = ""; + } + + this.emitter = new events.EventEmitter(); + this.source = source; + this.setLines(lines); + this.prereg = true; + + this.line = 0; + this.char = 1; + this.from = 1; + this.input = ""; + + for (var i = 0; i < state.option.indent; i += 1) { + state.tab += " "; + } +} + +Lexer.prototype = { + _lines: [], + + getLines: function () { + this._lines = state.lines; + return this._lines; + }, + + setLines: function (val) { + this._lines = val; + state.lines = this._lines; + }, + peek: function (i) { + return this.input.charAt(i || 0); + }, + skip: function (i) { + i = i || 1; + this.char += i; + this.input = this.input.slice(i); + }, + on: function (names, listener) { + names.split(" ").forEach(function (name) { + this.emitter.on(name, listener); + }.bind(this)); + }, + trigger: function () { + this.emitter.emit.apply(this.emitter, Array.prototype.slice.call(arguments)); + }, + triggerAsync: function (type, args, checks, fn) { + checks.push(function () { + if (fn()) { + this.trigger(type, args); + } + }.bind(this)); + }, + scanPunctuator: function () { + var ch1 = this.peek(); + var ch2, ch3, ch4; + + switch (ch1) { + case ".": + if ((/^[0-9]$/).test(this.peek(1))) { + return null; + } + if (this.peek(1) === "." && this.peek(2) === ".") { + return { + type: Token.Punctuator, + value: "..." + }; + } + case "(": + case ")": + case ";": + case ",": + case "{": + case "}": + case "[": + case "]": + case ":": + case "~": + case "?": + return { + type: Token.Punctuator, + value: ch1 + }; + case "#": + return { + type: Token.Punctuator, + value: ch1 + }; + case "": + return null; + } + + ch2 = this.peek(1); + ch3 = this.peek(2); + ch4 = this.peek(3); + + if (ch1 === ">" && ch2 === ">" && ch3 === ">" && ch4 === "=") { + return { + type: Token.Punctuator, + value: ">>>=" + }; + } + + if (ch1 === "=" && ch2 === "=" && ch3 === "=") { + return { + type: Token.Punctuator, + value: "===" + }; + } + + if (ch1 === "!" && ch2 === "=" && ch3 === "=") { + return { + type: Token.Punctuator, + value: "!==" + }; + } + + if (ch1 === ">" && ch2 === ">" && ch3 === ">") { + return { + type: Token.Punctuator, + value: ">>>" + }; + } + + if (ch1 === "<" && ch2 === "<" && ch3 === "=") { + return { + type: Token.Punctuator, + value: "<<=" + }; + } + + if (ch1 === ">" && ch2 === ">" && ch3 === "=") { + return { + type: Token.Punctuator, + value: ">>=" + }; + } + if (ch1 === "=" && ch2 === ">") { + return { + type: Token.Punctuator, + value: ch1 + ch2 + }; + } + if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) { + return { + type: Token.Punctuator, + value: ch1 + ch2 + }; + } + + if ("<>=!+-*%&|^".indexOf(ch1) >= 0) { + if (ch2 === "=") { + return { + type: Token.Punctuator, + value: ch1 + ch2 + }; + } + + return { + type: Token.Punctuator, + value: ch1 + }; + } + + if (ch1 === "/") { + if (ch2 === "=" && /\/=(?!(\S*\/[gim]?))/.test(this.input)) { + return { + type: Token.Punctuator, + value: "/=" + }; + } + + return { + type: Token.Punctuator, + value: "/" + }; + } + + return null; + }, + scanComments: function () { + var ch1 = this.peek(); + var ch2 = this.peek(1); + var rest = this.input.substr(2); + var startLine = this.line; + var startChar = this.char; + + function commentToken(label, body, opt) { + var special = ["jshint", "jslint", "members", "member", "globals", "global", "exported"]; + var isSpecial = false; + var value = label + body; + var commentType = "plain"; + opt = opt || {}; + + if (opt.isMultiline) { + value += "*/"; + } + + special.forEach(function (str) { + if (isSpecial) { + return; + } + if (label === "//" && str !== "jshint") { + return; + } + + if (body.substr(0, str.length) === str) { + isSpecial = true; + label = label + str; + body = body.substr(str.length); + } + + if (!isSpecial && body.charAt(0) === " " && body.substr(1, str.length) === str) { + isSpecial = true; + label = label + " " + str; + body = body.substr(str.length + 1); + } + + if (!isSpecial) { + return; + } + + switch (str) { + case "member": + commentType = "members"; + break; + case "global": + commentType = "globals"; + break; + default: + commentType = str; + } + }); + + return { + type: Token.Comment, + commentType: commentType, + value: value, + body: body, + isSpecial: isSpecial, + isMultiline: opt.isMultiline || false, + isMalformed: opt.isMalformed || false + }; + } + if (ch1 === "*" && ch2 === "/") { + this.trigger("error", { + code: "E018", + line: startLine, + character: startChar + }); + + this.skip(2); + return null; + } + if (ch1 !== "/" || (ch2 !== "*" && ch2 !== "/")) { + return null; + } + if (ch2 === "/") { + this.skip(this.input.length); // Skip to the EOL. + return commentToken("//", rest); + } + + var body = ""; + if (ch2 === "*") { + this.skip(2); + + while (this.peek() !== "*" || this.peek(1) !== "/") { + if (this.peek() === "") { // End of Line + body += "\n"; + if (!this.nextLine()) { + this.trigger("error", { + code: "E017", + line: startLine, + character: startChar + }); + + return commentToken("/*", body, { + isMultiline: true, + isMalformed: true + }); + } + } else { + body += this.peek(); + this.skip(); + } + } + + this.skip(2); + return commentToken("/*", body, { isMultiline: true }); + } + }, + scanKeyword: function () { + var result = /^[a-zA-Z_$][a-zA-Z0-9_$]*/.exec(this.input); + var keywords = [ + "if", "in", "do", "var", "for", "new", + "try", "let", "this", "else", "case", + "void", "with", "enum", "while", "break", + "catch", "throw", "const", "yield", "class", + "super", "return", "typeof", "delete", + "switch", "export", "import", "default", + "finally", "extends", "function", "continue", + "debugger", "instanceof" + ]; + + if (result && keywords.indexOf(result[0]) >= 0) { + return { + type: Token.Keyword, + value: result[0] + }; + } + + return null; + }, + scanIdentifier: function () { + var id = ""; + var index = 0; + var type, char; + + function isUnicodeLetter(code) { + for (var i = 0; i < unicodeLetterTable.length;) { + if (code < unicodeLetterTable[i++]) { + return false; + } + + if (code <= unicodeLetterTable[i++]) { + return true; + } + } + + return false; + } + + function isHexDigit(str) { + return (/^[0-9a-fA-F]$/).test(str); + } + + var readUnicodeEscapeSequence = function () { + index += 1; + + if (this.peek(index) !== "u") { + return null; + } + + var ch1 = this.peek(index + 1); + var ch2 = this.peek(index + 2); + var ch3 = this.peek(index + 3); + var ch4 = this.peek(index + 4); + var code; + + if (isHexDigit(ch1) && isHexDigit(ch2) && isHexDigit(ch3) && isHexDigit(ch4)) { + code = parseInt(ch1 + ch2 + ch3 + ch4, 16); + + if (isUnicodeLetter(code)) { + index += 5; + return "\\u" + ch1 + ch2 + ch3 + ch4; + } + + return null; + } + + return null; + }.bind(this); + + var getIdentifierStart = function () { + var chr = this.peek(index); + var code = chr.charCodeAt(0); + + if (code === 92) { + return readUnicodeEscapeSequence(); + } + + if (code < 128) { + if (identifierStartTable[code]) { + index += 1; + return chr; + } + + return null; + } + + if (isUnicodeLetter(code)) { + index += 1; + return chr; + } + + return null; + }.bind(this); + + var getIdentifierPart = function () { + var chr = this.peek(index); + var code = chr.charCodeAt(0); + + if (code === 92) { + return readUnicodeEscapeSequence(); + } + + if (code < 128) { + if (identifierPartTable[code]) { + index += 1; + return chr; + } + + return null; + } + + if (isUnicodeLetter(code)) { + index += 1; + return chr; + } + + return null; + }.bind(this); + + char = getIdentifierStart(); + if (char === null) { + return null; + } + + id = char; + for (;;) { + char = getIdentifierPart(); + + if (char === null) { + break; + } + + id += char; + } + + switch (id) { + case "true": + case "false": + type = Token.BooleanLiteral; + break; + case "null": + type = Token.NullLiteral; + break; + default: + type = Token.Identifier; + } + + return { + type: type, + value: id + }; + }, + scanNumericLiteral: function () { + var index = 0; + var value = ""; + var length = this.input.length; + var char = this.peek(index); + var bad; + + function isDecimalDigit(str) { + return (/^[0-9]$/).test(str); + } + + function isOctalDigit(str) { + return (/^[0-7]$/).test(str); + } + + function isHexDigit(str) { + return (/^[0-9a-fA-F]$/).test(str); + } + + function isIdentifierStart(ch) { + return (ch === "$") || (ch === "_") || (ch === "\\") || + (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z"); + } + + if (char !== "." && !isDecimalDigit(char)) { + return null; + } + + if (char !== ".") { + value = this.peek(index); + index += 1; + char = this.peek(index); + + if (value === "0") { + if (char === "x" || char === "X") { + index += 1; + value += char; + + while (index < length) { + char = this.peek(index); + if (!isHexDigit(char)) { + break; + } + value += char; + index += 1; + } + + if (value.length <= 2) { // 0x + return { + type: Token.NumericLiteral, + value: value, + isMalformed: true + }; + } + + if (index < length) { + char = this.peek(index); + if (isIdentifierStart(char)) { + return null; + } + } + + return { + type: Token.NumericLiteral, + value: value, + base: 16, + isMalformed: false + }; + } + if (isOctalDigit(char)) { + index += 1; + value += char; + bad = false; + + while (index < length) { + char = this.peek(index); + + if (isDecimalDigit(char)) { + bad = true; + } else if (!isOctalDigit(char)) { + break; + } + value += char; + index += 1; + } + + if (index < length) { + char = this.peek(index); + if (isIdentifierStart(char)) { + return null; + } + } + + return { + type: Token.NumericLiteral, + value: value, + base: 8, + isMalformed: false + }; + } + + if (isDecimalDigit(char)) { + index += 1; + value += char; + } + } + + while (index < length) { + char = this.peek(index); + if (!isDecimalDigit(char)) { + break; + } + value += char; + index += 1; + } + } + + if (char === ".") { + value += char; + index += 1; + + while (index < length) { + char = this.peek(index); + if (!isDecimalDigit(char)) { + break; + } + value += char; + index += 1; + } + } + + if (char === "e" || char === "E") { + value += char; + index += 1; + char = this.peek(index); + + if (char === "+" || char === "-") { + value += this.peek(index); + index += 1; + } + + char = this.peek(index); + if (isDecimalDigit(char)) { + value += char; + index += 1; + + while (index < length) { + char = this.peek(index); + if (!isDecimalDigit(char)) { + break; + } + value += char; + index += 1; + } + } else { + return null; + } + } + + if (index < length) { + char = this.peek(index); + if (isIdentifierStart(char)) { + return null; + } + } + + return { + type: Token.NumericLiteral, + value: value, + base: 10, + isMalformed: !isFinite(value) + }; + }, + scanStringLiteral: function (checks) { + var quote = this.peek(); + if (quote !== "\"" && quote !== "'") { + return null; + } + this.triggerAsync("warning", { + code: "W108", + line: this.line, + character: this.char // +1? + }, checks, function () { return state.jsonMode && quote !== "\""; }); + + var value = ""; + var startLine = this.line; + var startChar = this.char; + var allowNewLine = false; + + this.skip(); + + while (this.peek() !== quote) { + while (this.peek() === "") { // End Of Line + + if (!allowNewLine) { + this.trigger("warning", { + code: "W112", + line: this.line, + character: this.char + }); + } else { + allowNewLine = false; + + this.triggerAsync("warning", { + code: "W043", + line: this.line, + character: this.char + }, checks, function () { return !state.option.multistr; }); + + this.triggerAsync("warning", { + code: "W042", + line: this.line, + character: this.char + }, checks, function () { return state.jsonMode && state.option.multistr; }); + } + + if (!this.nextLine()) { + this.trigger("error", { + code: "E029", + line: startLine, + character: startChar + }); + + return { + type: Token.StringLiteral, + value: value, + isUnclosed: true, + quote: quote + }; + } + } + + allowNewLine = false; + var char = this.peek(); + var jump = 1; // A length of a jump, after we're done + + if (char < " ") { + this.trigger("warning", { + code: "W113", + line: this.line, + character: this.char, + data: [ "" ] + }); + } + + if (char === "\\") { + this.skip(); + char = this.peek(); + + switch (char) { + case "'": + this.triggerAsync("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "\\'" ] + }, checks, function () {return state.jsonMode; }); + break; + case "b": + char = "\b"; + break; + case "f": + char = "\f"; + break; + case "n": + char = "\n"; + break; + case "r": + char = "\r"; + break; + case "t": + char = "\t"; + break; + case "0": + char = "\0"; + var n = parseInt(this.peek(1), 10); + this.triggerAsync("warning", { + code: "W115", + line: this.line, + character: this.char + }, checks, + function () { return n >= 0 && n <= 7 && state.directive["use strict"]; }); + break; + case "u": + char = String.fromCharCode(parseInt(this.input.substr(1, 4), 16)); + jump = 5; + break; + case "v": + this.triggerAsync("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "\\v" ] + }, checks, function () { return state.jsonMode; }); + + char = "\v"; + break; + case "x": + var x = parseInt(this.input.substr(1, 2), 16); + + this.triggerAsync("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "\\x-" ] + }, checks, function () { return state.jsonMode; }); + + char = String.fromCharCode(x); + jump = 3; + break; + case "\\": + case "\"": + case "/": + break; + case "": + allowNewLine = true; + char = ""; + break; + case "!": + if (value.slice(value.length - 2) === "<") { + break; + } + default: + this.trigger("warning", { + code: "W044", + line: this.line, + character: this.char + }); + } + } + + value += char; + this.skip(jump); + } + + this.skip(); + return { + type: Token.StringLiteral, + value: value, + isUnclosed: false, + quote: quote + }; + }, + scanRegExp: function () { + var index = 0; + var length = this.input.length; + var char = this.peek(); + var value = char; + var body = ""; + var flags = []; + var malformed = false; + var isCharSet = false; + var terminated; + + var scanUnexpectedChars = function () { + if (char < " ") { + malformed = true; + this.trigger("warning", { + code: "W048", + line: this.line, + character: this.char + }); + } + if (char === "<") { + malformed = true; + this.trigger("warning", { + code: "W049", + line: this.line, + character: this.char, + data: [ char ] + }); + } + }.bind(this); + if (!this.prereg || char !== "/") { + return null; + } + + index += 1; + terminated = false; + + while (index < length) { + char = this.peek(index); + value += char; + body += char; + + if (isCharSet) { + if (char === "]") { + if (this.peek(index - 1) !== "\\" || this.peek(index - 2) === "\\") { + isCharSet = false; + } + } + + if (char === "\\") { + index += 1; + char = this.peek(index); + body += char; + value += char; + + scanUnexpectedChars(); + } + + index += 1; + continue; + } + + if (char === "\\") { + index += 1; + char = this.peek(index); + body += char; + value += char; + + scanUnexpectedChars(); + + if (char === "/") { + index += 1; + continue; + } + + if (char === "[") { + index += 1; + continue; + } + } + + if (char === "[") { + isCharSet = true; + index += 1; + continue; + } + + if (char === "/") { + body = body.substr(0, body.length - 1); + terminated = true; + index += 1; + break; + } + + index += 1; + } + + if (!terminated) { + this.trigger("error", { + code: "E015", + line: this.line, + character: this.from + }); + + return void this.trigger("fatal", { + line: this.line, + from: this.from + }); + } + + while (index < length) { + char = this.peek(index); + if (!/[gim]/.test(char)) { + break; + } + flags.push(char); + value += char; + index += 1; + } + + try { + new RegExp(body, flags.join("")); + } catch (err) { + malformed = true; + this.trigger("error", { + code: "E016", + line: this.line, + character: this.char, + data: [ err.message ] // Platform dependent! + }); + } + + return { + type: Token.RegExp, + value: value, + flags: flags, + isMalformed: malformed + }; + }, + scanMixedSpacesAndTabs: function () { + var at, match; + + if (state.option.smarttabs) { + match = this.input.match(/(\/\/|^\s?\*)? \t/); + at = match && !match[1] ? 0 : -1; + } else { + at = this.input.search(/ \t|\t [^\*]/); + } + + return at; + }, + scanUnsafeChars: function () { + return this.input.search(reg.unsafeChars); + }, + next: function (checks) { + this.from = this.char; + var start; + if (/\s/.test(this.peek())) { + start = this.char; + + while (/\s/.test(this.peek())) { + this.from += 1; + this.skip(); + } + + if (this.peek() === "") { // EOL + if (!/^\s*$/.test(this.getLines()[this.line - 1]) && state.option.trailing) { + this.trigger("warning", { code: "W102", line: this.line, character: start }); + } + } + } + + var match = this.scanComments() || + this.scanStringLiteral(checks); + + if (match) { + return match; + } + + match = + this.scanRegExp() || + this.scanPunctuator() || + this.scanKeyword() || + this.scanIdentifier() || + this.scanNumericLiteral(); + + if (match) { + this.skip(match.value.length); + return match; + } + + return null; + }, + nextLine: function () { + var char; + + if (this.line >= this.getLines().length) { + return false; + } + + this.input = this.getLines()[this.line]; + this.line += 1; + this.char = 1; + this.from = 1; + + char = this.scanMixedSpacesAndTabs(); + if (char >= 0) { + this.trigger("warning", { code: "W099", line: this.line, character: char + 1 }); + } + + this.input = this.input.replace(/\t/g, state.tab); + char = this.scanUnsafeChars(); + + if (char >= 0) { + this.trigger("warning", { code: "W100", line: this.line, character: char }); + } + + if (state.option.maxlen && state.option.maxlen < this.input.length) { + this.trigger("warning", { code: "W101", line: this.line, character: this.input.length }); + } + + return true; + }, + start: function () { + this.nextLine(); + }, + token: function () { + var checks = asyncTrigger(); + var token; + + + function isReserved(token, isProperty) { + if (!token.reserved) { + return false; + } + + if (token.meta && token.meta.isFutureReservedWord) { + if (state.option.inES5(true) && !token.meta.es5) { + return false; + } + if (token.meta.strictOnly) { + if (!state.option.strict && !state.directive["use strict"]) { + return false; + } + } + + if (isProperty) { + return false; + } + } + + return true; + } + var create = function (type, value, isProperty) { + var obj; + + if (type !== "(endline)" && type !== "(end)") { + this.prereg = false; + } + + if (type === "(punctuator)") { + switch (value) { + case ".": + case ")": + case "~": + case "#": + case "]": + this.prereg = false; + break; + default: + this.prereg = true; + } + + obj = Object.create(state.syntax[value] || state.syntax["(error)"]); + } + + if (type === "(identifier)") { + if (value === "return" || value === "case" || value === "typeof") { + this.prereg = true; + } + + if (_.has(state.syntax, value)) { + obj = Object.create(state.syntax[value] || state.syntax["(error)"]); + if (!isReserved(obj, isProperty && type === "(identifier)")) { + obj = null; + } + } + } + + if (!obj) { + obj = Object.create(state.syntax[type]); + } + + obj.identifier = (type === "(identifier)"); + obj.type = obj.type || type; + obj.value = value; + obj.line = this.line; + obj.character = this.char; + obj.from = this.from; + + if (isProperty && obj.identifier) { + obj.isProperty = isProperty; + } + + obj.check = checks.check; + + return obj; + }.bind(this); + + for (;;) { + if (!this.input.length) { + return create(this.nextLine() ? "(endline)" : "(end)", ""); + } + + token = this.next(checks); + + if (!token) { + if (this.input.length) { + this.trigger("error", { + code: "E024", + line: this.line, + character: this.char, + data: [ this.peek() ] + }); + + this.input = ""; + } + + continue; + } + + switch (token.type) { + case Token.StringLiteral: + this.triggerAsync("String", { + line: this.line, + char: this.char, + from: this.from, + value: token.value, + quote: token.quote + }, checks, function () { return true; }); + + return create("(string)", token.value); + case Token.Identifier: + this.trigger("Identifier", { + line: this.line, + char: this.char, + from: this.form, + name: token.value, + isProperty: state.tokens.curr.id === "." + }); + case Token.Keyword: + case Token.NullLiteral: + case Token.BooleanLiteral: + return create("(identifier)", token.value, state.tokens.curr.id === "."); + + case Token.NumericLiteral: + if (token.isMalformed) { + this.trigger("warning", { + code: "W045", + line: this.line, + character: this.char, + data: [ token.value ] + }); + } + + this.triggerAsync("warning", { + code: "W114", + line: this.line, + character: this.char, + data: [ "0x-" ] + }, checks, function () { return token.base === 16 && state.jsonMode; }); + + this.triggerAsync("warning", { + code: "W115", + line: this.line, + character: this.char + }, checks, function () { + return state.directive["use strict"] && token.base === 8; + }); + + this.trigger("Number", { + line: this.line, + char: this.char, + from: this.from, + value: token.value, + base: token.base, + isMalformed: token.malformed + }); + + return create("(number)", token.value); + + case Token.RegExp: + return create("(regexp)", token.value); + + case Token.Comment: + state.tokens.curr.comment = true; + + if (token.isSpecial) { + return { + value: token.value, + body: token.body, + type: token.commentType, + isSpecial: token.isSpecial, + line: this.line, + character: this.char, + from: this.from + }; + } + + break; + + case "": + break; + + default: + return create("(punctuator)", token.value); + } + } + } +}; + +exports.Lexer = Lexer; + +})() +}, +{"events":2,"./reg.js":4,"./state.js":5,"underscore":12}], +12:[function(req,module,exports){ +(function(){// Underscore.js 1.4.4 + +(function() { + var root = this; + var previousUnderscore = root._; + var breaker = {}; + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + var push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + _.VERSION = '1.4.4'; + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + for (var key in obj) { + if (_.has(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) return; + } + } + } + }; + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results[results.length] = iterator.call(context, value, index, list); + }); + return results; + }; + + var reduceError = 'Reduce of empty array with no initial value'; + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + _.reject = function(obj, iterator, context) { + return _.filter(obj, function(value, index, list) { + return !iterator.call(context, value, index, list); + }, context); + }; + _.every = _.all = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + _.contains = _.include = function(obj, target) { + if (obj == null) return false; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + return any(obj, function(value) { + return value === target; + }); + }; + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + return (isFunc ? method : value[method]).apply(value, args); + }); + }; + _.pluck = function(obj, key) { + return _.map(obj, function(value){ return value[key]; }); + }; + _.where = function(obj, attrs, first) { + if (_.isEmpty(attrs)) return first ? null : []; + return _[first ? 'find' : 'filter'](obj, function(value) { + for (var key in attrs) { + if (attrs[key] !== value[key]) return false; + } + return true; + }); + }; + _.findWhere = function(obj, attrs) { + return _.where(obj, attrs, true); + }; + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return -Infinity; + var result = {computed : -Infinity, value: -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed >= result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return Infinity; + var result = {computed : Infinity, value: Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + var lookupIterator = function(value) { + return _.isFunction(value) ? value : function(obj){ return obj[value]; }; + }; + _.sortBy = function(obj, value, context) { + var iterator = lookupIterator(value); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + index : index, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index < right.index ? -1 : 1; + }), 'value'); + }; + var group = function(obj, value, context, behavior) { + var result = {}; + var iterator = lookupIterator(value || _.identity); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + _.groupBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + (_.has(result, key) ? result[key] : (result[key] = [])).push(value); + }); + }; + _.countBy = function(obj, value, context) { + return group(obj, value, context, function(result, key) { + if (!_.has(result, key)) result[key] = 0; + result[key]++; + }); + }; + _.sortedIndex = function(array, obj, iterator, context) { + iterator = iterator == null ? _.identity : lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (obj.length === +obj.length) return _.map(obj, _.identity); + return _.values(obj); + }; + _.size = function(obj) { + if (obj == null) return 0; + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + _.last = function(array, n, guard) { + if (array == null) return void 0; + if ((n != null) && !guard) { + return slice.call(array, Math.max(array.length - n, 0)); + } else { + return array[array.length - 1]; + } + }; + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + _.compact = function(array) { + return _.filter(array, _.identity); + }; + var flatten = function(input, shallow, output) { + each(input, function(value) { + if (_.isArray(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + _.uniq = _.unique = function(array, isSorted, iterator, context) { + if (_.isFunction(isSorted)) { + context = iterator; + iterator = isSorted; + isSorted = false; + } + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + _.union = function() { + return _.uniq(concat.apply(ArrayProto, arguments)); + }; + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + _.zip = function() { + var args = slice.call(arguments); + var length = _.max(_.pluck(args, 'length')); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(args, "" + i); + } + return results; + }; + _.object = function(list, values) { + if (list == null) return {}; + var result = {}; + for (var i = 0, l = list.length; i < l; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, l = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < l; i++) if (array[i] === item) return i; + return -1; + }; + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var len = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(len); + + while(idx < len) { + range[idx++] = start; + start += step; + } + + return range; + }; + _.bind = function(func, context) { + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + var args = slice.call(arguments, 2); + return function() { + return func.apply(context, args.concat(slice.call(arguments))); + }; + }; + _.partial = function(func) { + var args = slice.call(arguments, 1); + return function() { + return func.apply(this, args.concat(slice.call(arguments))); + }; + }; + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length === 0) funcs = _.functions(obj); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + _.throttle = function(func, wait) { + var context, args, timeout, result; + var previous = 0; + var later = function() { + previous = new Date; + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + _.debounce = function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) result = func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) result = func.apply(context, args); + return result; + }; + }; + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + _.wrap = function(func, wrapper) { + return function() { + var args = [func]; + push.apply(args, arguments); + return wrapper.apply(this, args); + }; + }; + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + _.after = function(times, func) { + if (times <= 0) return func(); + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; + return keys; + }; + _.values = function(obj) { + var values = []; + for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); + return values; + }; + _.pairs = function(obj) { + var pairs = []; + for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); + return pairs; + }; + _.invert = function(obj) { + var result = {}; + for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; + return result; + }; + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; + }; + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + } + }); + return obj; + }; + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + var eq = function(a, b, aStack, bStack) { + if (a === b) return a !== 0 || 1 / a == 1 / b; + if (a == null || b == null) return a === b; + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + case '[object String]': + return a == String(b); + case '[object Number]': + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + return +a == +b; + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + var length = aStack.length; + while (length--) { + if (aStack[length] == a) return bStack[length] == b; + } + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + if (className == '[object Array]') { + size = a.length; + result = size == b.length; + if (result) { + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor))) { + return false; + } + for (var key in a) { + if (_.has(a, key)) { + size++; + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + aStack.pop(); + bStack.pop(); + return result; + }; + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + _.isObject = function(obj) { + return obj === Object(obj); + }; + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + _.isNull = function(obj) { + return obj === null; + }; + _.isUndefined = function(obj) { + return obj === void 0; + }; + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + _.identity = function(value) { + return value; + }; + _.times = function(n, iterator, context) { + var accum = Array(n); + for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); + return accum; + }; + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + _.result = function(object, property) { + if (object == null) return null; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + var noMatch = /(.)^/; + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + _.chain = function(obj) { + return _(obj).chain(); + }; + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + _.mixin(_); + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + chain: function() { + this._chain = true; + return this; + }, + value: function() { + return this._wrapped; + } + + }); + +}).call(this); + +})() +}, +{}] +},{},["E/GbHF"]) +; + +function req() {return require.apply(this, arguments)} +module.exports = req("jshint"); + }); \ No newline at end of file diff --git a/lib/client/key.js b/lib/client/key.js index a0de62e3..85ac4ff7 100644 --- a/lib/client/key.js +++ b/lib/client/key.js @@ -1,5 +1,5 @@ var CloudCmd, Util, DOM; -(function(CloudCmd, Util, DOM){ +(function(CloudCmd, Util, DOM) { 'use strict'; var KEY = { @@ -46,7 +46,7 @@ var CloudCmd, Util, DOM; KeyProto.prototype = KEY; CloudCmd.Key = new KeyProto(CloudCmd, Util, DOM); - function KeyProto(CloudCmd, Util, DOM){ + function KeyProto(CloudCmd, Util, DOM) { var Key = this, Binded, lTabPanel = { @@ -55,20 +55,20 @@ var CloudCmd, Util, DOM; }; - this.isBind = function(){return Binded;}; + this.isBind = function() {return Binded;}; - this.setBind = function(){Binded = true;}; + this.setBind = function() {Binded = true;}; - this.unsetBind = function(){Binded = false;}; + this.unsetBind = function() {Binded = false;}; - this.bind = function(){ + this.bind = function() { DOM.Events.addKey(listener); /* клавиши назначены*/ Binded = true; }; - function listener(pEvent){ + function listener(pEvent) { /* получаем выдленный файл*/ var i, n, lCurrent = DOM.getCurrentFile(), lKeyCode = pEvent.keyCode, @@ -76,255 +76,250 @@ var CloudCmd, Util, DOM; lAlt = pEvent.altKey, lCtrl = pEvent.ctrlKey; /* если клавиши можно обрабатывать*/ - if(Binded){ - switch(lKeyCode){ - case Key.TAB: - /* changing parent panel of curent-file */ - var lPanel = DOM.getPanel(), - lId = lPanel.id; - - lTabPanel[lId] = lCurrent; - - lPanel = DOM.getPanel({active:false}); + if (Binded) { + switch (lKeyCode) { + case Key.TAB: + /* changing parent panel of curent-file */ + var lPanel = DOM.getPanel(), lId = lPanel.id; - - if(lTabPanel[lId]) - DOM.setCurrentFile(lTabPanel[lId]); - else{ - var lFirstFileOnList = DOM.getByTag('li', lPanel)[2]; - - DOM.setCurrentFile(lFirstFileOnList); - } - - DOM.preventDefault(pEvent);//запрет на дальнейшее действие - break; - case Key.INSERT: - DOM.setSelectedFile( lCurrent ); - DOM.setCurrentFile( lCurrent.nextSibling ); - break; + lTabPanel[lId] = lCurrent; - case Key.DELETE: - if(lShift){ - var lUrl = DOM.getCurrentPath(lCurrent); + lPanel = DOM.getPanel({active:false}); + lId = lPanel.id; + + if (lTabPanel[lId]) + DOM.setCurrentFile(lTabPanel[lId]); + else{ + var lFirstFileOnList = DOM.getByTag('li', lPanel)[2]; - if( DOM.isCurrentIsDir(lCurrent) ) - lUrl += '?dir'; - - DOM.RESTfull.delete(lUrl, function(){ - DOM.deleteCurrent(lCurrent); - }); - } - else - DOM.promptDeleteSelected(lCurrent); - break; + DOM.setCurrentFile(lFirstFileOnList); + } - case Key.F1: - Util.exec(CloudCmd.Help); - DOM.preventDefault(pEvent); - break; - - case Key.F2: - DOM.renameCurrent(lCurrent); - break; + DOM.preventDefault(pEvent);//запрет на дальнейшее действие + break; + + case Key.INSERT: + DOM .toggleSelectedFile(lCurrent) + .setCurrentFile(lCurrent.nextSibling); + break; + + case Key.DELETE: + if (lShift) { + var lUrl = DOM.getCurrentPath(lCurrent); - case Key.F3: - Util.exec(CloudCmd.View); - DOM.preventDefault(pEvent); - break; - - case Key.F4: - Util.exec(CloudCmd.Edit); - DOM.preventDefault(pEvent); - break; - - case Key.F5: - DOM.copyCurrent(lCurrent); - DOM.preventDefault(pEvent); - break; - - case Key.F6: - DOM.moveCurrent(lCurrent); - DOM.preventDefault(pEvent); - break; - - case Key.F7: - DOM.promptNewDir(); - break; - - case Key.F8: - DOM.promptDeleteSelected(lCurrent); - break; - - case Key.F: - DOM.promptDeleteCurrent(lCurrent); - break; - - case Key.F9: - Util.exec(CloudCmd.Menu); - DOM.preventDefault(pEvent); - break; - - case Key.TRA: - DOM.Images.showLoad({top: true}); - Util.exec(CloudCmd.Console); - break; + if ( DOM.isCurrentIsDir(lCurrent) ) + lUrl += '?dir'; - case Key.SPACE: - var lSelected = DOM.isSelected(lCurrent), - lDir = DOM.isCurrentIsDir(lCurrent), - lName = DOM.getCurrentName(lCurrent); - - if(!lDir || lName === '..') - lSelected = true; - - Util.ifExec(lSelected, function(){ - DOM.setSelectedFile(lCurrent); - }, function(pCallBack){ - DOM.loadCurrentSize(pCallBack, lCurrent); + DOM.RESTfull.delete(lUrl, function() { + DOM.deleteCurrent(lCurrent); }); - - - DOM.preventDefault(pEvent); - break; - - /* навигация по таблице файлов * - * если нажали клавишу вверх * - * выделяем предыдущую строку */ - case Key.UP: - if(lShift) - DOM.setSelectedFile(lCurrent); - - DOM.setCurrentFile( lCurrent.previousSibling ); - DOM.preventDefault( pEvent ); - break; - - /* если нажали клавишу в низ * - * выделяем следующую строку */ - case Key.DOWN: - if(lShift) - DOM.setSelectedFile(lCurrent); - - DOM.setCurrentFile( lCurrent.nextSibling ); - DOM.preventDefault( pEvent ); - break; - - /* если нажали клавишу Home * - * переходим к самому верхнему * - * элементу */ - case Key.HOME: - DOM.setCurrentFile( lCurrent.parentElement.firstChild ); - DOM.preventDefault(pEvent); - break; - - /* если нажали клавишу End - * выделяем последний элемент */ - case Key.END: - DOM.setCurrentFile( lCurrent.parentElement.lastElementChild ); - DOM.preventDefault( pEvent ); - break; - - /* если нажали клавишу page down - * проматываем экран */ - case Key.PAGE_DOWN: - DOM.scrollByPages( DOM.getPanel(), 1 ); - - for(i=0; i<30; i++){ - if(!lCurrent.nextSibling) break; - - lCurrent = lCurrent.nextSibling; - } - DOM.setCurrentFile(lCurrent); - DOM.preventDefault(pEvent); - break; - - /* если нажали клавишу page up проматываем экран */ - case Key.PAGE_UP: - DOM.scrollByPages( DOM.getPanel(), -1 ); - - var lC = lCurrent, - tryCatch = function(pCurrentFile){ - Util.tryCatch(function(){ - return pCurrentFile - .previousSibling - .previousSibling - .previousSibling - .previousSibling; - }); - }; - - for(i = 0; i < 30; i++){ - if(!lC.previousSibling || tryCatch(lC) ) break; - - lC = lC.previousSibling; - } - DOM.setCurrentFile(lC); - DOM.preventDefault(pEvent); - break; - - /* открываем папку*/ - case Key.ENTER: - if( DOM.isCurrentIsDir() ) - Util.exec( CloudCmd.loadDir() ); - break; - - case Key.A: - if(pEvent.ctrlKey){ - var lParent = lCurrent.parentElement, - lNodes = lParent.childNodes; - - /* not path and fm_header */ - for(i = 2, n = lNodes.length; i < n; i++) - DOM.setSelectedFile( lNodes[i] ); - - DOM.preventDefault(pEvent); - } - - break; - - /* - * обновляем страницу, - * загружаем содержимое каталога - * при этом данные берём всегда с - * сервера, а не из кэша - * (обновляем кэш) - */ - case Key.R: - if(lCtrl){ - Util.log('+r pressed\n' + - 'reloading page...\n' + - 'press +q to remove all key-handlers'); - - CloudCmd.refresh(); - DOM.preventDefault(pEvent); - } + } + else + DOM.promptDeleteSelected(lCurrent); + break; + + case Key.F1: + Util.exec(CloudCmd.Help); + DOM.preventDefault(pEvent); + break; + + case Key.F2: + DOM.renameCurrent(lCurrent); break; - /* чистим кэш */ - case Key.D: - if(lCtrl){ - Util.log('+d pressed\n' + - 'clearing cache...\n' + - 'press +q to remove all key-handlers'); - - DOM.Cache.clear(); - DOM.preventDefault(); - } - break; + case Key.F3: + Util.exec(CloudCmd.View); + DOM.preventDefault(pEvent); + break; - /* убираем все обработчики - * нажатий клавиш */ - case Key.Q: - if(lAlt){ - Util.log('+q pressed\n' + - '+r reload key-handerl - removed' + - '+s clear cache key-handler - removed'+ - 'press +s to to set them'); - - /* обработчик нажатий клавиш снят*/ - Binded = false; - DOM.preventDefault(pEvent); - } + case Key.F4: + Util.exec(CloudCmd.Edit); + DOM.preventDefault(pEvent); + break; + + case Key.F5: + DOM.copyCurrent(lCurrent); + DOM.preventDefault(pEvent); + break; + + case Key.F6: + DOM.moveCurrent(lCurrent); + DOM.preventDefault(pEvent); + break; + + case Key.F7: + DOM.promptNewDir(); + break; + + case Key.F8: + DOM.promptDeleteSelected(lCurrent); + break; + + case Key.F9: + Util.exec(CloudCmd.Menu); + DOM.preventDefault(pEvent); + + break; + + case Key.TRA: + DOM.Images.showLoad({top: true}); + Util.exec(CloudCmd.Console); + + break; + + case Key.SPACE: + var lSelected = DOM.isSelected(lCurrent), + lDir = DOM.isCurrentIsDir(lCurrent), + lName = DOM.getCurrentName(lCurrent); + + if (!lDir || lName === '..') + lSelected = true; + + Util.ifExec(lSelected, function() { + DOM.toggleSelectedFile(lCurrent); + }, function(pCallBack) { + DOM.loadCurrentSize(pCallBack, lCurrent); + }); + + + DOM.preventDefault(pEvent); + break; + + /* навигация по таблице файлов * + * если нажали клавишу вверх * + * выделяем предыдущую строку */ + case Key.UP: + if (lShift) + DOM.toggleSelectedFile(lCurrent); + + DOM.setCurrentFile( lCurrent.previousSibling ); + DOM.preventDefault( pEvent ); + break; + + /* если нажали клавишу в низ - выделяем следующую строку */ + case Key.DOWN: + if (lShift) + DOM.toggleSelectedFile(lCurrent); + + DOM.setCurrentFile( lCurrent.nextSibling ); + DOM.preventDefault( pEvent ); + break; + + /* если нажали клавишу Home * + * переходим к самому верхнему * + * элементу */ + case Key.HOME: + DOM.setCurrentFile( lCurrent.parentElement.firstChild ); + DOM.preventDefault(pEvent); + break; + + /* если нажали клавишу End выделяем последний элемент */ + case Key.END: + DOM.setCurrentFile( lCurrent.parentElement.lastElementChild ); + DOM.preventDefault( pEvent ); + break; + + /* если нажали клавишу page down проматываем экран */ + case Key.PAGE_DOWN: + DOM.scrollByPages( DOM.getPanel(), 1 ); + + for (i = 0; i < 30; i++) { + + if (!lCurrent.nextSibling) + break; + + lCurrent = lCurrent.nextSibling; + } + DOM.setCurrentFile(lCurrent); + DOM.preventDefault(pEvent); + break; + + /* если нажали клавишу page up проматываем экран */ + case Key.PAGE_UP: + DOM.scrollByPages(DOM.getPanel(), -1); + + var lC = lCurrent, + tryCatch = function(pCurrentFile) { + Util.tryCatch(function() { + return pCurrentFile + .previousSibling + .previousSibling + .previousSibling + .previousSibling; + }); + }; + + for (i = 0; i < 30; i++) { + if (!lC.previousSibling || tryCatch(lC) ) break; + + lC = lC.previousSibling; + } + + DOM.setCurrentFile(lC); + DOM.preventDefault(pEvent); + break; + + /* открываем папку*/ + case Key.ENTER: + if (DOM.isCurrentIsDir()) + Util.exec( CloudCmd.loadDir() ); + break; + + case Key.A: + if (pEvent.ctrlKey) { + var lParent = lCurrent.parentElement, + lNodes = lParent.childNodes; + + /* not path and fm_header */ + for (i = 2, n = lNodes.length; i < n; i++) + DOM.toggleSelectedFile( lNodes[i] ); + + DOM.preventDefault(pEvent); + } + break; + + /* + * обновляем страницу, + * загружаем содержимое каталога + * при этом данные берём всегда с + * сервера, а не из кэша + * (обновляем кэш) + */ + case Key.R: + if (lCtrl) { + Util.log('+r pressed\n' + + 'reloading page...\n' + + 'press +q to remove all key-handlers'); + + CloudCmd.refresh(); + DOM.preventDefault(pEvent); + } + break; + + /* чистим кэш */ + case Key.D: + if (lCtrl) { + Util.log('+d pressed\n' + + 'clearing cache...\n' + + 'press +q to remove all key-handlers'); + + DOM.Cache.clear(); + DOM.preventDefault(); + } + break; + + /* убираем все обработчики нажатий клавиш */ + case Key.Q: + if (lAlt) { + Util.log('+q pressed\n' + + '+r reload key-handerl - removed' + + '+s clear cache key-handler - removed'+ + 'press +s to to set them'); + + Binded = false; + DOM.preventDefault(pEvent); + } break; } } @@ -332,13 +327,14 @@ var CloudCmd, Util, DOM; /* устанавливаем все обработчики * нажатий клавиш */ - else if(lKeyCode === Key.S && lAlt){ + else if (lKeyCode === Key.S && lAlt) { /* обрабатываем нажатия на клавиши*/ Binded = true; - Util.log('+s pressed\n' + - '+r reload key-handerl - set\n' + - '+s clear cache key-handler - set\n' + + Util.log('+s pressed \n' + + '+r reload key-handerl - set \n' + + '+s clear cache key-handler - set \n' + 'press +q to remove them'); + DOM.preventDefault(pEvent); } } diff --git a/lib/client/listeners.js b/lib/client/listeners.js new file mode 100644 index 00000000..f74dbcb9 --- /dev/null +++ b/lib/client/listeners.js @@ -0,0 +1,291 @@ +var Util, DOM, CloudCmd; + +(function (Util, DOM) { + 'use strict'; + + CloudCmd.Listeners = new ListenersProto(CloudCmd, Util, DOM); + + function ListenersProto(CloudCmd, Util, DOM){ + var Cache = DOM.Cache, + Events = DOM.Events, + getConfig = CloudCmd.getConfig; + + this.analytics = function() { + getConfig(function(config) { + if (config.analytics) { + Events.addOneTime('mousemove', function(){ + var FIVE_SECONDS = 5000, + lUrl = CloudCmd.LIBDIRCLIENT + 'analytics.js'; + + setTimeout(function(){ + DOM.jsload(lUrl); + }, FIVE_SECONDS); + }); + } + }); + }; + + this.init = function () { + appCache(); + contextMenu(); + dragndrop(); + unload(); + pop(); + }; + + this.initKeysPanel = function() { + var i, lButton, lEl, + lKeysPanel = {}, + + lFuncs =[ + null, + CloudCmd.Help, /* f1 */ + DOM.renameCurrent, /* f2 */ + CloudCmd.View, /* f3 */ + CloudCmd.Edit, /* f4 */ + DOM.copyCurrent, /* f5 */ + DOM.moveCurrent, /* f6 */ + DOM.promptNewDir, /* f7 */ + DOM.promptDeleteSelected, /* f8 */ + CloudCmd.Menu, /* f9 */ + ]; + + for (i = 1; i <= 9; i++) { + lButton = 'f' + i, + lEl = DOM.getById('f' + i); + lKeysPanel[lButton] = lEl; + + if (i === 1 || i === 3 || i === 4 || i === 9) + Events.addOneTime('click', lFuncs[i], lEl); + else + Events.addClick(lFuncs[i], lEl); + } + + lButton = '~', + lEl = DOM.getById('~'); + lKeysPanel[lButton] = lEl; + Events.addOneTime('click', CloudCmd.Console, lEl); + + return lKeysPanel; + }; + + /** + * функция меняет ссыки на ajax-овые + * @param pPanelID + */ + this.changeLinks = function(pPanelID){ + /* назначаем кнопку очистить кэш и показываем её */ + var lClearcache = DOM.getById('clear-cache'); + Events.addClick(Cache.clear, lClearcache); + + /* меняем ссылки на ajax-запросы */ + var lPanel = DOM.getById(pPanelID), + a = DOM.getByTag('a', lPanel), + + /* right mouse click function varible */ + lOnContextMenu_f = function(pEvent){ + var lReturn_b = true, + Key = CloudCmd.Key; + + Key && Key.unsetBind(); + + /* getting html element + * currentTarget - DOM event + * target - jquery event + */ + var lTarget = pEvent.currentTarget || pEvent.target; + DOM.setCurrentFile(lTarget); + + if(Util.isFunction(CloudCmd.Menu) ){ + CloudCmd.Menu({ + x: pEvent.clientX, + y: pEvent.clientY + }); + + /* disabling browsers menu*/ + lReturn_b = false; + DOM.Images.showLoad(); + } + + return lReturn_b; + }, + + /* drag and drop function varible + * download file from browser to descktop + * in Chrome (HTML5) + */ + lOnDragStart_f = function(pEvent){ + var lElement = pEvent.target, + lLink = lElement.href, + lName = lElement.textContent; + + /* if it's directory - adding json extension */ + if( DOM.isCurrentIsDir() ){ + lName += '.json'; + lLink += '?json'; + } + + pEvent.dataTransfer.setData("DownloadURL", + 'application/octet-stream' + ':' + + lName + ':' + + lLink); + }, + + lSetCurrentFile_f = function(pEvent){ + var pElement = pEvent.target, + lTag = pElement.tagName; + + if(lTag !== 'LI') + do{ + pElement = pElement.parentElement; + lTag = pElement.tagName; + }while(lTag !== 'LI'); + + DOM.setCurrentFile(pElement); + }, + + lUrl = CloudCmd.HOST, + lLoadDirOnce = CloudCmd.loadDir(); + + /* ставим загрузку гифа на клик*/ + Events.addClick( CloudCmd.refresh, a[0].parentElement ); + + /* start from 1 cous 0 is a path and it's setted up */ + for(var i = 1, n = a.length; i < n ; i++){ + /* убираем адрес хоста*/ + var ai = a[i], + lLink = Util.removeStr(ai.href, lUrl), + lLoadDir = CloudCmd.loadDir(lLink), + /* устанавливаем обработчики на строку + * на двойное нажатие на левую кнопку мышки */ + lLi = ai.parentElement.parentElement; + + /* if we in path - set click event */ + if (lLi.className === 'path') + Events.addClick( lLoadDir, ai ); + else { + Events.add({ + 'click' : DOM.preventDefault, + 'mousedown' : lSetCurrentFile_f, + 'contextmenu' : lOnContextMenu_f + }, lLi); + + Events.add('dragstart', lOnDragStart_f, ai); + + /* если ссылка на папку, а не файл */ + if(ai.target !== '_blank'){ + Events.add({ + 'dblclick' : lLoadDirOnce, + 'touchend' : lLoadDirOnce + }, lLi); + } + + lLi.id = (ai.title ? ai.title : ai.textContent) + + '(' + pPanelID + ')'; + } + } + }; + + function appCache() { + getConfig(function(config) { + var isAppCache = config.appcache, + appCache = window.applicationCache; + + if (isAppCache && appCache) + Events.add('updateready', function() { + var ret = confirm('An update is available. Reload now?'); + + if (ret) + location.reload(); + + }, appCache); + }); + } + + function contextMenu() { + Events.addContextMenu(function(pEvent){ + CloudCmd.Menu.ENABLED || DOM.preventDefault(pEvent); + }, document); + } + + function dragndrop() { + var panels = DOM.getByClass('panel'), + i = 0, + n = panels.length, + preventDefault = function (event) { + event.stopPropagation(); + event.preventDefault(); + }, + toggle = function () { + for (i = 0; i < n; i++) + DOM.toggleClass(panels[i], 'selected-panel'); + }, + onDrop = function (event) { + var reader, file, files, + dir = DOM.getCurrentDirPath(), + load = function(file){ + return function(event) { + var path = dir + file.name, + data = event.target.result; + + DOM.RESTfull.save(path, data, CloudCmd.refresh); + }; + }; + + preventDefault(event); + toggle(); + + files = event.dataTransfer.files; + + if (files.length) { + n = files.length; + + for (i = 0; i < n; i++) { + reader = new FileReader(); + file = files[i]; + Events.add('load', load(file), reader); + reader.readAsArrayBuffer(file); + } + } + }; + + Events.add(['dragenter', 'dragleave'], toggle); + + for (i = 0; i < n; i++) { + Events.add('dragover', preventDefault, panels[i]); + Events.add('drop', onDrop, panels[i]); + } + } + + function unload() { + DOM.Events.add(['unload', 'beforeunload'], function (pEvent) { + var lRet, + Key = CloudCmd.Key, + lIsBind = Key && Key.isBind(); + + if(!lIsBind) { + DOM.preventDefault(pEvent); + lRet = 'Please make sure that you saved all work.'; + } + + return lRet; + }); + } + + function pop() { + Events.add("popstate", function(pEvent) { + var lPath = pEvent.state + '?json'; + + if (pEvent.state) { + lPath = pEvent.state + '?json'; + ajaxLoad(lPath, {nohistory: true}); + } else + CloudCmd.route(location.hash); + + return true; + }); + } + + } + +})(Util, DOM); diff --git a/lib/client/menu.js b/lib/client/menu.js index 945cc392..24a04b7c 100644 --- a/lib/client/menu.js +++ b/lib/client/menu.js @@ -9,7 +9,9 @@ var CloudCmd, Util, DOM, CloudFunc, $; CloudCmd.Menu = new MenuProto(CloudCmd, Util, DOM, CloudFunc); function MenuProto(CloudCmd, Util, DOM, CloudFunc) { - var Key = CloudCmd.Key, + var Name = 'Menu', + Loading = false, + Key = CloudCmd.Key, Events = DOM.Events, MenuSeted = false, Menu = this, @@ -19,6 +21,8 @@ var CloudCmd, Util, DOM, CloudFunc, $; this.ENABLED = false; this.init = function(pPosition){ + Loading = true; + Position = pPosition; Util.loadOnLoad([ @@ -40,13 +44,15 @@ var CloudCmd, Util, DOM, CloudFunc, $; }; this.show = function() { - set(); - DOM.Images.hideLoad(); - - if(Position && !Position.x ) - Position = undefined; - - $('li').contextMenu(Position); + if (!Loading) { + set(); + DOM.Images.hideLoad(); + + if(Position && !Position.x ) + Position = undefined; + + $('li').contextMenu(Position); + } }; /* function read data from modules.json @@ -173,7 +179,8 @@ var CloudCmd, Util, DOM, CloudFunc, $; 'Rename' : function(){ setTimeout( Util.retExec(DOM.renameCurrent), 100); }, - 'Delete' : Util.retExec(DOM.promptDeleteSelected) + 'Delete' : Util.retExec(DOM.promptDeleteSelected), + 'Zip file' : DOM.zipFile }; if (UploadToItemNames.length) @@ -185,7 +192,7 @@ var CloudCmd, Util, DOM, CloudFunc, $; 'File' : DOM.promptNewFile, 'Dir' : DOM.promptNewDir, - 'From cloud...' : function(){ + 'From Filepicker' : function(){ CloudCmd.execFromModule('FilePicker', 'saveFile', function(pName, pData){ var lPath = DOM.getCurrentDirPath() + pName; @@ -227,7 +234,7 @@ var CloudCmd, Util, DOM, CloudFunc, $; * @param pCallBack */ function load(pCallBack){ - Util.time('menu load'); + Util.time(Name + ' load'); var lDir = '/lib/client/menu/', lFiles = [ @@ -236,7 +243,8 @@ var CloudCmd, Util, DOM, CloudFunc, $; ]; DOM.anyLoadInParallel(lFiles, function(){ - Util.timeEnd('menu load'); + Util.timeEnd(Name + ' load'); + Loading = false; Util.exec(pCallBack); }); } diff --git a/lib/client/socket.js b/lib/client/socket.js index 18fe07b6..a513fea1 100644 --- a/lib/client/socket.js +++ b/lib/client/socket.js @@ -16,7 +16,14 @@ var CloudCmd, Util, DOM, io; }); function connect() { - socket = io.connect(CloudCmd.HOST); + socket = io.connect(CloudCmd.HOST, { + 'reconnect' : true, + 'reconnection delay' : 500, + 'max reconnection attempts' : Math.pow(2, 64), + 'reconnect_failed' : function() { + Util.log('Could not reconnect. Reload page.'); + } + }); CloudCmd.Socket = socket; diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js index d64edeef..8ca3a8c7 100644 --- a/lib/client/storage/_dropbox.js +++ b/lib/client/storage/_dropbox.js @@ -104,7 +104,7 @@ var CloudCmd, Util, DOM, Dropbox, cb, Client; load ]); - CloudCmd.DropBox.init = null; + delete DropBoxStore.init; }; CloudCmd.DropBox = DropBoxStore; diff --git a/lib/client/storage/_filepicker.js b/lib/client/storage/_filepicker.js index a1b0e9d6..936743b4 100644 --- a/lib/client/storage/_filepicker.js +++ b/lib/client/storage/_filepicker.js @@ -16,20 +16,25 @@ var CloudCmd, Util, DOM, $, filepicker; var lContent = pParams.data, lName = pParams.name; - filepicker.store(lContent, {filename: lName}, function(pFPFile){ - console.log(pFPFile); - - filepicker.exportFile(pFPFile, Util.log, Util.log); + filepicker.store(lContent, { + mimetype: '', + filename: lName + }, + function(pFPFile){ + Util.log(pFPFile); + + filepicker.exportFile(pFPFile, Util.log, Util.log); }); }; this.saveFile = function(pCallBack){ filepicker.pick(function(FPFile){ - console.log(FPFile); + Util.log(FPFile); DOM.ajax({ - url : FPFile.url, - success : function(pData){ + url : FPFile.url, + responseType :'arraybuffer', + success : function(pData){ Util.exec(pCallBack, FPFile.filename, pData); } }); @@ -37,7 +42,7 @@ var CloudCmd, Util, DOM, $, filepicker; }; function load(pCallBack){ - console.time('filepicker load'); + Util.time('filepicker load'); var lHTTP = document.location.protocol; DOM.jsload(lHTTP + '//api.filepicker.io/v1/filepicker.js', function(){ @@ -48,7 +53,7 @@ var CloudCmd, Util, DOM, $, filepicker; filepicker.setKey(lKey); DOM.Images.hideLoad(); - console.timeEnd('filepicker loaded'); + Util.timeEnd('filepicker loaded'); Util.exec(pCallBack); }); }); diff --git a/lib/client/view.js b/lib/client/view.js index cd824045..403ea2ae 100644 --- a/lib/client/view.js +++ b/lib/client/view.js @@ -6,7 +6,9 @@ var CloudCmd, Util, DOM, CloudFunc, $; CloudCmd.View = new ViewProto(CloudCmd, Util, DOM, CloudFunc); function ViewProto(CloudCmd, Util, DOM, CloudFunc){ - var Key = CloudCmd.Key, + var Name = 'View', + Loading = false, + Key = CloudCmd.Key, Images = DOM.Images, View = this, Element, @@ -27,6 +29,8 @@ var CloudCmd, Util, DOM, CloudFunc, $; autoSize : false, height : window.innerHeight, width : window.innerWidth/0.75, + minWidth : 0, + minHeight : 0, helpers : { overlay : { @@ -41,6 +45,7 @@ var CloudCmd, Util, DOM, CloudFunc, $; this.init = function(pCallBack){ var lFunc, lIsFunc, lIsCallBack; + Loading = true; if (pCallBack){ lIsFunc = Util.isFunction(pCallBack); lIsCallBack = Util.isFunction(pCallBack.callback); @@ -70,28 +75,31 @@ var CloudCmd, Util, DOM, CloudFunc, $; var lPath, lElement, lAfterFunc, lFunc; - Element = $('
'); - if (pData) { - lElement = $(Element).append(pData); - lAfterFunc = Config.afterShow, - lFunc = function(){ - Util.exec(lAfterFunc); - Util.exec(pCallBack); - }; - - Config.afterShow = lFunc; - - $.fancybox(lElement, Config); - - } else { - lPath = CloudFunc.FS + DOM.getCurrentPath(); - if( Util.checkExtension(lPath, ['png','jpg', 'gif','ico']) ) { - $.fancybox.open({ href : lPath }, Config); + if (!Loading) { + Element = $('
'); + if (pData) { + lElement = $(Element).append(pData); + lAfterFunc = Config.afterShow, + lFunc = function(){ + Util.exec(lAfterFunc); + Util.exec(pCallBack); + }; + + Config.afterShow = lFunc; + + $.fancybox(lElement, Config); + + } else { + lPath = CloudFunc.FS + DOM.getCurrentPath(); + if( Util.checkExtension(lPath, ['png','jpg', 'gif','ico']) ) { + $.fancybox.open({ href : lPath }, Config); + } + else + DOM.getCurrentData(function(pParams){ + var data = document.createTextNode(pParams.data); + $.fancybox( Element.append(data), Config ); + }); } - else - DOM.getCurrentData(function(pParams){ - $.fancybox( Element.append(pParams.data), Config ); - }); } }; @@ -105,13 +113,14 @@ var CloudCmd, Util, DOM, CloudFunc, $; * @pCallBack - executes, when everything loaded */ function load(pCallBack){ - Util.time('fancybox load'); + Util.time(Name + ' load'); var lDir = CloudCmd.LIBDIRCLIENT + 'view/fancyBox/source/', lFiles = [ lDir + 'jquery.fancybox.css', lDir + 'jquery.fancybox.js' ]; DOM.anyLoadOnLoad([lFiles], function(){ - Util.timeEnd('fancybox load'); + Util.timeEnd(Name + ' load'); + Loading = false; Util.exec( pCallBack ); Images.hideLoad(); }) diff --git a/lib/client/view/fancyBox/CHANGELOG.md b/lib/client/view/fancyBox/CHANGELOG.md index 869e0fe1..b0d847e2 100644 --- a/lib/client/view/fancyBox/CHANGELOG.md +++ b/lib/client/view/fancyBox/CHANGELOG.md @@ -1,6 +1,12 @@ fancyBox - Changelog ========= +### Version 2.1.5 - June 14, 2013 +* Fixed #493 - Broken slideshow +* Fixed #556 - Parent option +* Retina graphics (#564) and retina display support (#420) +* Improved "lock" feature + ### Version 2.1.4 - January 10, 2013 * Update to be compatible with jQuery v1.9 * Small changes that should fix usability issues for certain users diff --git a/lib/client/view/fancyBox/demo/index.html b/lib/client/view/fancyBox/demo/index.html index 757314ff..70397a8b 100644 --- a/lib/client/view/fancyBox/demo/index.html +++ b/lib/client/view/fancyBox/demo/index.html @@ -5,14 +5,14 @@ - + - - + + @@ -23,7 +23,7 @@ - +