├── .gitignore ├── reports ├── assets │ ├── font │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff │ ├── css │ │ ├── vendor │ │ │ ├── morris.css │ │ │ └── codemirror.css │ │ ├── plato.css │ │ ├── plato-file.css │ │ └── plato-overview.css │ └── scripts │ │ ├── vendor │ │ ├── codemirror │ │ │ └── util │ │ │ │ ├── simple-hint.css │ │ │ │ ├── dialog.css │ │ │ │ ├── colorize.js │ │ │ │ ├── continuelist.js │ │ │ │ ├── continuecomment.js │ │ │ │ ├── runmode.js │ │ │ │ ├── match-highlighter.js │ │ │ │ ├── loadmode.js │ │ │ │ ├── overlay.js │ │ │ │ ├── dialog.js │ │ │ │ ├── matchbrackets.js │ │ │ │ ├── runmode-standalone.js │ │ │ │ ├── multiplex.js │ │ │ │ ├── xml-hint.js │ │ │ │ ├── closetag.js │ │ │ │ ├── formatting.js │ │ │ │ ├── simple-hint.js │ │ │ │ ├── searchcursor.js │ │ │ │ ├── pig-hint.js │ │ │ │ ├── search.js │ │ │ │ ├── javascript-hint.js │ │ │ │ └── foldcode.js │ │ ├── jquery.fittext.js │ │ ├── bootstrap-popover.js │ │ └── bootstrap-tooltip.js │ │ ├── codemirror.markpopovertext.js │ │ ├── plato-sortable-file-list.js │ │ ├── plato-file.js │ │ └── plato-overview.js ├── report.history.json ├── report.history.js ├── files │ ├── amd_js │ │ ├── report.history.json │ │ ├── report.history.js │ │ ├── report.json │ │ ├── report.js │ │ └── index.html │ └── backbone_picky_js │ │ ├── report.history.json │ │ └── report.history.js ├── coverage │ └── prettify.css ├── report.json ├── report.js └── index.html ├── .jshintrc ├── package.json ├── src └── amd.js ├── bower.json ├── spec └── javascripts │ ├── support │ ├── jasmine.yml │ └── karma.conf.js │ ├── helpers │ └── SpecHelper.js │ ├── multiSelect.spec.js │ ├── multiSelect.toggleSelectAll.spec.js │ ├── selectable.spec.js │ └── multiSelect.deselectAll.spec.js ├── readme.md ├── Gruntfile.js └── lib ├── backbone.picky.min.js ├── amd └── backbone.picky.min.js └── backbone.picky.map /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | *.swo 4 | *.orig 5 | tmp/ 6 | ext/ 7 | node_modules 8 | _SpecRunner.html 9 | .idea/ 10 | -------------------------------------------------------------------------------- /reports/assets/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashchange/backbone.picky/master/reports/assets/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /reports/assets/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashchange/backbone.picky/master/reports/assets/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /reports/assets/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashchange/backbone.picky/master/reports/assets/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": false, 3 | "eqeqeq": true, 4 | "immed": false, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "boss": true, 11 | "eqnull": true, 12 | "expr": true, 13 | "browser": true, 14 | "globals": { 15 | "jQuery": true, 16 | "Backbone": true, 17 | "_": true, 18 | "$": true, 19 | "slice": true, 20 | "throwError": true 21 | } 22 | } -------------------------------------------------------------------------------- /reports/report.history.json: -------------------------------------------------------------------------------- 1 | [{"date":"Tue, 25 Jun 2013 00:30:30 GMT","total":{"sloc":201,"maintainability":142.76385056199274},"average":{"sloc":100,"maintainability":"71.38"}},{"date":"Mon, 03 Feb 2014 09:05:23 GMT","total":{"sloc":522,"maintainability":139.1668435209674},"average":{"sloc":261,"maintainability":"69.58"}},{"date":"Tue, 04 Feb 2014 00:16:28 GMT","total":{"sloc":532,"maintainability":139.45075435605347},"average":{"sloc":266,"maintainability":"69.73"}}] -------------------------------------------------------------------------------- /reports/assets/css/vendor/morris.css: -------------------------------------------------------------------------------- 1 | .morris-hover{position:absolute;z-index:1000;}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255, 255, 255, 0.8);border:solid 2px rgba(230, 230, 230, 0.8);font-family:sans-serif;font-size:12px;text-align:center;}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0;} 2 | .morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0;} -------------------------------------------------------------------------------- /reports/report.history.js: -------------------------------------------------------------------------------- 1 | __history = [{"date":"Tue, 25 Jun 2013 00:30:30 GMT","total":{"sloc":201,"maintainability":142.76385056199274},"average":{"sloc":100,"maintainability":"71.38"}},{"date":"Mon, 03 Feb 2014 09:05:23 GMT","total":{"sloc":522,"maintainability":139.1668435209674},"average":{"sloc":261,"maintainability":"69.58"}},{"date":"Tue, 04 Feb 2014 00:16:28 GMT","total":{"sloc":532,"maintainability":139.45075435605347},"average":{"sloc":266,"maintainability":"69.73"}}] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backbone.picky", 3 | "version": "0.2.0", 4 | "dependencies": {}, 5 | "devDependencies": { 6 | "matchdep": "~0.1.2", 7 | "grunt": "~0.4.1", 8 | "grunt-plato": "~0.2.0", 9 | "grunt-preprocess": "~2.3.0", 10 | "grunt-contrib-concat": "~0.3.0", 11 | "grunt-contrib-jshint": "~0.6.0", 12 | "grunt-contrib-uglify": "~0.2.2", 13 | "grunt-contrib-jasmine": "~0.5.1", 14 | "grunt-template-jasmine-istanbul": "~0.2.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/simple-hint.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-completions { 2 | position: absolute; 3 | z-index: 10; 4 | overflow: hidden; 5 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 6 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 7 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 8 | } 9 | .CodeMirror-completions select { 10 | background: #fafafa; 11 | outline: none; 12 | border: none; 13 | padding: 0; 14 | margin: 0; 15 | font-family: monospace; 16 | } 17 | -------------------------------------------------------------------------------- /src/amd.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof exports === 'object') { 3 | 4 | var underscore = require('underscore'); 5 | var backbone = require('backbone'); 6 | 7 | module.exports = factory(underscore, backbone); 8 | 9 | } else if (typeof define === 'function' && define.amd) { 10 | 11 | define(['underscore', 'backbone'], factory); 12 | 13 | } 14 | }(this, function (_, Backbone) { 15 | "option strict"; 16 | 17 | // @include backbone.picky.js 18 | return Backbone.Picky; 19 | 20 | })); 21 | 22 | -------------------------------------------------------------------------------- /reports/files/amd_js/report.history.json: -------------------------------------------------------------------------------- 1 | [{"date":"Tue, 25 Jun 2013 00:30:30 GMT","sloc":20,"lloc":11,"functions":2,"deliveredBugs":0.09412972989735063,"maintainability":72.10710664828852,"lintErrors":5,"difficulty":10.105263157894736},{"date":"Mon, 03 Feb 2014 09:05:23 GMT","sloc":20,"lloc":11,"functions":2,"deliveredBugs":0.09412972989735063,"maintainability":72.10710664828852,"lintErrors":5,"difficulty":10.105263157894736},{"date":"Tue, 04 Feb 2014 00:16:28 GMT","sloc":20,"lloc":11,"functions":2,"deliveredBugs":0.09412972989735063,"maintainability":72.10710664828852,"lintErrors":5,"difficulty":10.105263157894736}] -------------------------------------------------------------------------------- /reports/files/amd_js/report.history.js: -------------------------------------------------------------------------------- 1 | __history = [{"date":"Tue, 25 Jun 2013 00:30:30 GMT","sloc":20,"lloc":11,"functions":2,"deliveredBugs":0.09412972989735063,"maintainability":72.10710664828852,"lintErrors":5,"difficulty":10.105263157894736},{"date":"Mon, 03 Feb 2014 09:05:23 GMT","sloc":20,"lloc":11,"functions":2,"deliveredBugs":0.09412972989735063,"maintainability":72.10710664828852,"lintErrors":5,"difficulty":10.105263157894736},{"date":"Tue, 04 Feb 2014 00:16:28 GMT","sloc":20,"lloc":11,"functions":2,"deliveredBugs":0.09412972989735063,"maintainability":72.10710664828852,"lintErrors":5,"difficulty":10.105263157894736}] -------------------------------------------------------------------------------- /reports/files/backbone_picky_js/report.history.json: -------------------------------------------------------------------------------- 1 | [{"date":"Tue, 25 Jun 2013 00:30:30 GMT","sloc":181,"lloc":89,"functions":17,"deliveredBugs":0.8341643454213132,"maintainability":70.65674391370422,"lintErrors":0,"difficulty":61.41176470588235},{"date":"Mon, 03 Feb 2014 09:05:23 GMT","sloc":502,"lloc":300,"functions":48,"deliveredBugs":4.710534746862121,"maintainability":67.05973687267888,"lintErrors":0,"difficulty":98.82246376811594},{"date":"Tue, 04 Feb 2014 00:16:28 GMT","sloc":512,"lloc":307,"functions":50,"deliveredBugs":4.769192165604345,"maintainability":67.34364770776494,"lintErrors":0,"difficulty":97.6063829787234}] -------------------------------------------------------------------------------- /reports/files/backbone_picky_js/report.history.js: -------------------------------------------------------------------------------- 1 | __history = [{"date":"Tue, 25 Jun 2013 00:30:30 GMT","sloc":181,"lloc":89,"functions":17,"deliveredBugs":0.8341643454213132,"maintainability":70.65674391370422,"lintErrors":0,"difficulty":61.41176470588235},{"date":"Mon, 03 Feb 2014 09:05:23 GMT","sloc":502,"lloc":300,"functions":48,"deliveredBugs":4.710534746862121,"maintainability":67.05973687267888,"lintErrors":0,"difficulty":98.82246376811594},{"date":"Tue, 04 Feb 2014 00:16:28 GMT","sloc":512,"lloc":307,"functions":50,"deliveredBugs":4.769192165604345,"maintainability":67.34364770776494,"lintErrors":0,"difficulty":97.6063829787234}] -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backbone.picky", 3 | "version": "0.2.0", 4 | "homepage": "https://github.com/derickbailey/backbone.picky", 5 | "authors": [ 6 | "Derick Bailey " 7 | ], 8 | "description": "selectable entities as mixins for Backbone.Model and Backbone.Collection", 9 | "main": "lib/backbone.picky.js", 10 | "keywords": [ 11 | "backbone" 12 | ], 13 | "license": "MIT", 14 | "ignore": [ 15 | "Gruntfile.js", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "public", 20 | "reports", 21 | "spec", 22 | "src", 23 | "tests" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /reports/coverage/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/dialog.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-dialog { 2 | position: absolute; 3 | left: 0; right: 0; 4 | background: white; 5 | z-index: 15; 6 | padding: .1em .8em; 7 | overflow: hidden; 8 | color: #333; 9 | } 10 | 11 | .CodeMirror-dialog-top { 12 | border-bottom: 1px solid #eee; 13 | top: 0; 14 | } 15 | 16 | .CodeMirror-dialog-bottom { 17 | border-top: 1px solid #eee; 18 | bottom: 0; 19 | } 20 | 21 | .CodeMirror-dialog input { 22 | border: none; 23 | outline: none; 24 | background: transparent; 25 | width: 20em; 26 | color: inherit; 27 | font-family: monospace; 28 | } 29 | 30 | .CodeMirror-dialog button { 31 | font-size: 70%; 32 | } 33 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/colorize.js: -------------------------------------------------------------------------------- 1 | CodeMirror.colorize = (function() { 2 | 3 | var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/; 4 | 5 | function textContent(node, out) { 6 | if (node.nodeType == 3) return out.push(node.nodeValue); 7 | for (var ch = node.firstChild; ch; ch = ch.nextSibling) { 8 | textContent(ch, out); 9 | if (isBlock.test(node.nodeType)) out.push("\n"); 10 | } 11 | } 12 | 13 | return function(collection, defaultMode) { 14 | if (!collection) collection = document.body.getElementsByTagName("pre"); 15 | 16 | for (var i = 0; i < collection.length; ++i) { 17 | var node = collection[i]; 18 | var mode = node.getAttribute("data-lang") || defaultMode; 19 | if (!mode) continue; 20 | 21 | var text = []; 22 | textContent(node, text); 23 | node.innerHTML = ""; 24 | CodeMirror.runMode(text.join(""), mode, node); 25 | 26 | node.className += " cm-s-default"; 27 | } 28 | }; 29 | })(); 30 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/continuelist.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { 3 | var pos = cm.getCursor(), token = cm.getTokenAt(pos); 4 | var space; 5 | if (token.className == "string") { 6 | var full = cm.getRange({line: pos.line, ch: 0}, {line: pos.line, ch: token.end}); 7 | var listStart = /\*|\d+\./, listContinue; 8 | if (token.string.search(listStart) == 0) { 9 | var reg = /^[\W]*(\d+)\./g; 10 | var matches = reg.exec(full); 11 | if(matches) 12 | listContinue = (parseInt(matches[1]) + 1) + ". "; 13 | else 14 | listContinue = "* "; 15 | space = full.slice(0, token.start); 16 | if (!/^\s*$/.test(space)) { 17 | space = ""; 18 | for (var i = 0; i < token.start; ++i) space += " "; 19 | } 20 | } 21 | } 22 | 23 | if (space != null) 24 | cm.replaceSelection("\n" + space + listContinue, "end"); 25 | else 26 | cm.execCommand("newlineAndIndent"); 27 | }; 28 | })(); 29 | -------------------------------------------------------------------------------- /reports/assets/css/plato.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | } 4 | 5 | .navbar { 6 | margin-bottom:0; 7 | padding: 0 20px; 8 | background-color: #f2f2f2; 9 | background-image: none; 10 | border: 1px solid #d4d4d4; 11 | border-radius: 4px; 12 | -webkit-box-shadow: none; 13 | box-shadow: none; 14 | line-height:10px; 15 | } 16 | 17 | .navbar-nav > .active > a { 18 | background-color: rgb(233, 233, 233); 19 | } 20 | 21 | a:visited { 22 | fill:inherit; 23 | } 24 | 25 | .jumbotron { 26 | color:#333; 27 | } 28 | 29 | li { 30 | line-height: 10px; 31 | } 32 | 33 | /* Landscape phone to portrait tablet */ 34 | @media (max-width: 767px) { 35 | .jumbotron h1 { 36 | font-size: 40px; 37 | } 38 | } 39 | 40 | .aggregate-stats { 41 | 42 | } 43 | 44 | .group-header { 45 | text-align:center; 46 | } 47 | 48 | 49 | .aggregate-stats .header { 50 | text-align: center; 51 | color: #5a5a5a; 52 | font-weight:lighter; 53 | } 54 | 55 | .aggregate-stats .stat { 56 | text-align: center; 57 | color: #5a5a5a; 58 | font-size:55px; 59 | line-height:70px; 60 | } 61 | 62 | i.icon[rel=popover] { 63 | font-size:23px; 64 | color: #0088cc; 65 | } 66 | 67 | .popover { 68 | z-index:100000; 69 | } -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/jquery.fittext.js: -------------------------------------------------------------------------------- 1 | /*global jQuery */ 2 | /*! 3 | * FitText.js 1.1 4 | * 5 | * Copyright 2011, Dave Rupert http://daverupert.com 6 | * Released under the WTFPL license 7 | * http://sam.zoy.org/wtfpl/ 8 | * 9 | * Date: Thu May 05 14:23:00 2011 -0600 10 | */ 11 | 12 | (function( $ ){ 13 | 14 | $.fn.fitText = function( kompressor, options ) { 15 | 16 | // Setup options 17 | var compressor = kompressor || 1, 18 | settings = $.extend({ 19 | 'minFontSize' : Number.NEGATIVE_INFINITY, 20 | 'maxFontSize' : Number.POSITIVE_INFINITY 21 | }, options); 22 | 23 | return this.each(function(){ 24 | 25 | // Store the object 26 | var $this = $(this); 27 | 28 | // Resizer() resizes items based on the object width divided by the compressor * 10 29 | var resizer = function () { 30 | $this.css('font-size', Math.max(Math.min($this.width() / (compressor*10), parseFloat(settings.maxFontSize)), parseFloat(settings.minFontSize))); 31 | }; 32 | 33 | // Call once to set. 34 | resizer(); 35 | 36 | // Call on resize. Opera debounces their resize by default. 37 | $(window).on('resize', resizer); 38 | 39 | }); 40 | 41 | }; 42 | 43 | })( jQuery ); -------------------------------------------------------------------------------- /reports/report.json: -------------------------------------------------------------------------------- 1 | {"summary":{"total":{"sloc":532,"maintainability":139.45075435605347},"average":{"sloc":266,"maintainability":"69.73"}},"reports":[{"info":{"file":"src/amd.js","fileShort":"amd.js","fileSafe":"amd_js","link":"files/amd_js/index.html"},"jshint":{"messages":5},"complexity":{"aggregate":{"line":1,"complexity":{"sloc":{"physical":20,"logical":11},"cyclomatic":3,"halstead":{"operators":{"distinct":12,"total":25,"identifiers":["__stripped__"]},"operands":{"distinct":19,"total":32,"identifiers":["__stripped__"]},"length":57,"vocabulary":31,"difficulty":10.105263157894736,"volume":282.3891896920519,"effort":2853.61707478284,"bugs":0.09412972989735063,"time":158.53428193238},"params":4}},"module":"amd.js","maintainability":72.10710664828852}},{"info":{"file":"src/backbone.picky.js","fileShort":"backbone.picky.js","fileSafe":"backbone_picky_js","link":"files/backbone_picky_js/index.html"},"jshint":{"messages":0},"complexity":{"aggregate":{"line":1,"complexity":{"sloc":{"physical":512,"logical":307},"cyclomatic":59,"halstead":{"operators":{"distinct":25,"total":839,"identifiers":["__stripped__"]},"operands":{"distinct":141,"total":1101,"identifiers":["__stripped__"]},"length":1940,"vocabulary":166,"difficulty":97.6063829787234,"volume":14307.576496813035,"effort":1396510.7910453149,"bugs":4.769192165604345,"time":77583.93283585082},"params":74}},"module":"backbone.picky.js","maintainability":67.34364770776494}}]} -------------------------------------------------------------------------------- /reports/report.js: -------------------------------------------------------------------------------- 1 | __report = {"summary":{"total":{"sloc":532,"maintainability":139.45075435605347},"average":{"sloc":266,"maintainability":"69.73"}},"reports":[{"info":{"file":"src/amd.js","fileShort":"amd.js","fileSafe":"amd_js","link":"files/amd_js/index.html"},"jshint":{"messages":5},"complexity":{"aggregate":{"line":1,"complexity":{"sloc":{"physical":20,"logical":11},"cyclomatic":3,"halstead":{"operators":{"distinct":12,"total":25,"identifiers":["__stripped__"]},"operands":{"distinct":19,"total":32,"identifiers":["__stripped__"]},"length":57,"vocabulary":31,"difficulty":10.105263157894736,"volume":282.3891896920519,"effort":2853.61707478284,"bugs":0.09412972989735063,"time":158.53428193238},"params":4}},"module":"amd.js","maintainability":72.10710664828852}},{"info":{"file":"src/backbone.picky.js","fileShort":"backbone.picky.js","fileSafe":"backbone_picky_js","link":"files/backbone_picky_js/index.html"},"jshint":{"messages":0},"complexity":{"aggregate":{"line":1,"complexity":{"sloc":{"physical":512,"logical":307},"cyclomatic":59,"halstead":{"operators":{"distinct":25,"total":839,"identifiers":["__stripped__"]},"operands":{"distinct":141,"total":1101,"identifiers":["__stripped__"]},"length":1940,"vocabulary":166,"difficulty":97.6063829787234,"volume":14307.576496813035,"effort":1396510.7910453149,"bugs":4.769192165604345,"time":77583.93283585082},"params":74}},"module":"backbone.picky.js","maintainability":67.34364770776494}}]} -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine.yml: -------------------------------------------------------------------------------- 1 | # src_files 2 | # 3 | # Return an array of filepaths relative to src_dir to include before jasmine specs. 4 | # Default: [] 5 | # 6 | # EXAMPLE: 7 | # 8 | # src_files: 9 | # - lib/source1.js 10 | # - lib/source2.js 11 | # - dist/**/*.js 12 | # 13 | src_files: 14 | - public/javascripts/underscore.js 15 | - public/javascripts/backbone.js 16 | - src/backbone.picky.js 17 | - spec/javascripts/helpers/*.js 18 | - src/**/*.js 19 | 20 | # stylesheets 21 | # 22 | # Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs. 23 | # Default: [] 24 | # 25 | # EXAMPLE: 26 | # 27 | # stylesheets: 28 | # - css/style.css 29 | # - stylesheets/*.css 30 | # 31 | stylesheets: 32 | 33 | # helpers 34 | # 35 | # Return an array of filepaths relative to spec_dir to include before jasmine specs. 36 | # Default: ["helpers/**/*.js"] 37 | # 38 | # EXAMPLE: 39 | # 40 | # helpers: 41 | # - helpers/**/*.js 42 | # 43 | helpers: 44 | 45 | # spec_files 46 | # 47 | # Return an array of filepaths relative to spec_dir to include. 48 | # Default: ["**/*[sS]pec.js"] 49 | # 50 | # EXAMPLE: 51 | # 52 | # spec_files: 53 | # - **/*[sS]pec.js 54 | # 55 | spec_files: 56 | 57 | # src_dir 58 | # 59 | # Source directory path. Your src_files must be returned relative to this path. Will use root if left blank. 60 | # Default: project root 61 | # 62 | # EXAMPLE: 63 | # 64 | # src_dir: public 65 | # 66 | src_dir: 67 | 68 | # spec_dir 69 | # 70 | # Spec directory path. Your spec_files must be returned relative to this path. 71 | # Default: spec/javascripts 72 | # 73 | # EXAMPLE: 74 | # 75 | # spec_dir: spec/javascripts 76 | # 77 | spec_dir: 78 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/continuecomment.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var modes = ["clike", "css", "javascript"]; 3 | for (var i = 0; i < modes.length; ++i) 4 | CodeMirror.extendMode(modes[i], {blockCommentStart: "/*", 5 | blockCommentEnd: "*/", 6 | blockCommentContinue: " * "}); 7 | 8 | CodeMirror.commands.newlineAndIndentContinueComment = function(cm) { 9 | var pos = cm.getCursor(), token = cm.getTokenAt(pos); 10 | var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; 11 | var space; 12 | 13 | if (token.type == "comment" && mode.blockCommentStart) { 14 | var end = token.string.indexOf(mode.blockCommentEnd); 15 | var full = cm.getRange({line: pos.line, ch: 0}, {line: pos.line, ch: token.end}), found; 16 | if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { 17 | // Comment ended, don't continue it 18 | } else if (token.string.indexOf(mode.blockCommentStart) == 0) { 19 | space = full.slice(0, token.start); 20 | if (!/^\s*$/.test(space)) { 21 | space = ""; 22 | for (var i = 0; i < token.start; ++i) space += " "; 23 | } 24 | } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && 25 | found + mode.blockCommentContinue.length > token.start && 26 | /^\s*$/.test(full.slice(0, found))) { 27 | space = full.slice(0, found); 28 | } 29 | } 30 | 31 | if (space != null) 32 | cm.replaceSelection("\n" + space + mode.blockCommentContinue, "end"); 33 | else 34 | cm.execCommand("newlineAndIndent"); 35 | }; 36 | })(); 37 | -------------------------------------------------------------------------------- /reports/assets/css/plato-file.css: -------------------------------------------------------------------------------- 1 | .historical .chart { 2 | height: 200px; 3 | } 4 | 5 | .CodeMirror { 6 | height: auto; 7 | } 8 | 9 | .CodeMirror-scroll { 10 | overflow-x: hidden; 11 | overflow-y: hidden; 12 | } 13 | .CodeMirror-lines { 14 | cursor:default; 15 | } 16 | 17 | .plato-mark { 18 | background-color:rgb(212, 250, 236); 19 | border: 1px dashed red; 20 | border-width:1px 0 1px 0; 21 | cursor:pointer; 22 | } 23 | 24 | .plato-mark.focus { 25 | background-color: rgb(235, 250, 166); 26 | } 27 | .plato-mark.active { 28 | background-color: rgb(158, 180, 255); 29 | } 30 | 31 | .plato-mark-start { 32 | border-left-width:1px; 33 | padding-left:1px; 34 | } 35 | .plato-mark-end { 36 | border-right-width:1px; 37 | padding-right:1px; 38 | } 39 | .plato-gutter { 40 | } 41 | 42 | .plato-gutter-icon { 43 | font-size:16px; 44 | cursor:pointer; 45 | color: #800000; 46 | text-align:center; 47 | } 48 | 49 | .plato-gutter-jshint, .plato-gutter-complexity { 50 | width:14px; 51 | } 52 | 53 | .charts { 54 | margin-top:1em; 55 | } 56 | 57 | .charts .header { 58 | font-weight:normal; 59 | text-align:center; 60 | } 61 | 62 | .chart-header { 63 | font-weight:normal; 64 | text-align:center; 65 | } 66 | 67 | .CodeMirror pre { 68 | z-index:4; 69 | } 70 | 71 | .CodeMirror-linewidget { 72 | background-color: hsl(240, 20%, 96%); 73 | font-size:12px; 74 | box-shadow:inset 10px 10px 10px -12px hsl(240, 20%, 17%); 75 | margin-top:10px; 76 | padding-top:5px; 77 | padding-left:5px; 78 | padding-bottom:2px; 79 | } 80 | 81 | .CodeMirror-linewidget ~ .CodeMirror-linewidget{ 82 | box-shadow:inset 10px 0px 10px -12px hsl(240, 20%, 17%); 83 | margin-top:0px; 84 | padding-top:0px; 85 | } 86 | 87 | .plato-line-widget { 88 | } -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/runmode.js: -------------------------------------------------------------------------------- 1 | CodeMirror.runMode = function(string, modespec, callback, options) { 2 | var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); 3 | 4 | if (callback.nodeType == 1) { 5 | var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; 6 | var node = callback, col = 0; 7 | node.innerHTML = ""; 8 | callback = function(text, style) { 9 | if (text == "\n") { 10 | node.appendChild(document.createElement("br")); 11 | col = 0; 12 | return; 13 | } 14 | var content = ""; 15 | // replace tabs 16 | for (var pos = 0;;) { 17 | var idx = text.indexOf("\t", pos); 18 | if (idx == -1) { 19 | content += text.slice(pos); 20 | col += text.length - pos; 21 | break; 22 | } else { 23 | col += idx - pos; 24 | content += text.slice(pos, idx); 25 | var size = tabSize - col % tabSize; 26 | col += size; 27 | for (var i = 0; i < size; ++i) content += " "; 28 | pos = idx + 1; 29 | } 30 | } 31 | 32 | if (style) { 33 | var sp = node.appendChild(document.createElement("span")); 34 | sp.className = "cm-" + style.replace(/ +/g, " cm-"); 35 | sp.appendChild(document.createTextNode(content)); 36 | } else { 37 | node.appendChild(document.createTextNode(content)); 38 | } 39 | }; 40 | } 41 | 42 | var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); 43 | for (var i = 0, e = lines.length; i < e; ++i) { 44 | if (i) callback("\n"); 45 | var stream = new CodeMirror.StringStream(lines[i]); 46 | while (!stream.eol()) { 47 | var style = mode.token(stream, state); 48 | callback(stream.current(), style, i, stream.start); 49 | stream.start = stream.pos; 50 | } 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /reports/files/amd_js/report.json: -------------------------------------------------------------------------------- 1 | {"info":{"file":"src/amd.js","fileShort":"amd.js","fileSafe":"amd_js","link":"files/amd_js/index.html"},"complexity":{"aggregate":{"line":1,"complexity":{"sloc":{"physical":20,"logical":11},"cyclomatic":3,"halstead":{"operators":{"distinct":12,"total":25,"identifiers":["__stripped__"]},"operands":{"distinct":19,"total":32,"identifiers":["__stripped__"]},"length":57,"vocabulary":31,"difficulty":10.105263157894736,"volume":282.3891896920519,"effort":2853.61707478284,"bugs":0.09412972989735063,"time":158.53428193238},"params":4}},"functions":[{"name":"","line":14,"complexity":{"sloc":{"physical":7,"logical":2},"cyclomatic":1,"halstead":{"operators":{"distinct":2,"total":2,"identifiers":["__stripped__"]},"operands":{"distinct":4,"total":5,"identifiers":["__stripped__"]},"length":7,"vocabulary":6,"difficulty":1.25,"volume":18.094737505048094,"effort":22.61842188131012,"bugs":0.006031579168349364,"time":1.2565789934061178},"params":2}},{"name":"","line":1,"complexity":{"sloc":{"physical":14,"logical":7},"cyclomatic":3,"halstead":{"operators":{"distinct":10,"total":20,"identifiers":["__stripped__"]},"operands":{"distinct":14,"total":24,"identifiers":["__stripped__"]},"length":44,"vocabulary":24,"difficulty":8.571428571428571,"volume":201.7383500317309,"effort":1729.1858574148362,"bugs":0.06724611667724363,"time":96.0658809674909},"params":2}}],"maintainability":72.10710664828852,"params":2,"module":"amd.js"},"jshint":{"messages":[{"severity":"error","line":4,"column":22,"message":"'require' is not defined.","source":"'{a}' is not defined."},{"severity":"error","line":5,"column":20,"message":"'require' is not defined.","source":"'{a}' is not defined."},{"severity":"error","line":7,"column":5,"message":"'module' is not defined.","source":"'{a}' is not defined."},{"severity":"error","line":9,"column":46,"message":"'define' is not defined.","source":"'{a}' is not defined."},{"severity":"error","line":11,"column":5,"message":"'define' is not defined.","source":"'{a}' is not defined."}]}} -------------------------------------------------------------------------------- /reports/files/amd_js/report.js: -------------------------------------------------------------------------------- 1 | __report = {"info":{"file":"src/amd.js","fileShort":"amd.js","fileSafe":"amd_js","link":"files/amd_js/index.html"},"complexity":{"aggregate":{"line":1,"complexity":{"sloc":{"physical":20,"logical":11},"cyclomatic":3,"halstead":{"operators":{"distinct":12,"total":25,"identifiers":["__stripped__"]},"operands":{"distinct":19,"total":32,"identifiers":["__stripped__"]},"length":57,"vocabulary":31,"difficulty":10.105263157894736,"volume":282.3891896920519,"effort":2853.61707478284,"bugs":0.09412972989735063,"time":158.53428193238},"params":4}},"functions":[{"name":"","line":14,"complexity":{"sloc":{"physical":7,"logical":2},"cyclomatic":1,"halstead":{"operators":{"distinct":2,"total":2,"identifiers":["__stripped__"]},"operands":{"distinct":4,"total":5,"identifiers":["__stripped__"]},"length":7,"vocabulary":6,"difficulty":1.25,"volume":18.094737505048094,"effort":22.61842188131012,"bugs":0.006031579168349364,"time":1.2565789934061178},"params":2}},{"name":"","line":1,"complexity":{"sloc":{"physical":14,"logical":7},"cyclomatic":3,"halstead":{"operators":{"distinct":10,"total":20,"identifiers":["__stripped__"]},"operands":{"distinct":14,"total":24,"identifiers":["__stripped__"]},"length":44,"vocabulary":24,"difficulty":8.571428571428571,"volume":201.7383500317309,"effort":1729.1858574148362,"bugs":0.06724611667724363,"time":96.0658809674909},"params":2}}],"maintainability":72.10710664828852,"params":2,"module":"amd.js"},"jshint":{"messages":[{"severity":"error","line":4,"column":22,"message":"'require' is not defined.","source":"'{a}' is not defined."},{"severity":"error","line":5,"column":20,"message":"'require' is not defined.","source":"'{a}' is not defined."},{"severity":"error","line":7,"column":5,"message":"'module' is not defined.","source":"'{a}' is not defined."},{"severity":"error","line":9,"column":46,"message":"'define' is not defined.","source":"'{a}' is not defined."},{"severity":"error","line":11,"column":5,"message":"'define' is not defined.","source":"'{a}' is not defined."}]}} -------------------------------------------------------------------------------- /spec/javascripts/helpers/SpecHelper.js: -------------------------------------------------------------------------------- 1 | var Logger = function () { 2 | this.reset(); 3 | }; 4 | 5 | _.extend(Logger.prototype, { 6 | log: function (content) { 7 | this.entries.push(content); 8 | }, 9 | reset: function () { 10 | this.entries = []; 11 | } 12 | }); 13 | 14 | beforeEach(function() { 15 | 16 | this.addMatchers({ 17 | 18 | /** 19 | * Matcher that checks to see if the actual, a Jasmine spy, was called with 20 | * parameters beginning with a specific set. 21 | * 22 | * @example 23 | * 24 | * spyOn(obj, "foo"); 25 | * obj.foo(1, 2, 3); 26 | * expect(obj.foo).toHaveBeenCalledWithInitial(1, 2); // => true 27 | */ 28 | toHaveBeenCalledWithInitial: function() { 29 | var expectedArgs = jasmine.util.argsToArray(arguments); 30 | if (!jasmine.isSpy(this.actual)) { 31 | throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 32 | } 33 | this.message = function() { 34 | var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with initial arguments " + jasmine.pp(expectedArgs) + " but it was."; 35 | var positiveMessage = ""; 36 | if (this.actual.callCount === 0) { 37 | positiveMessage = "Expected spy " + this.actual.identity + " to have been called with initial arguments " + jasmine.pp(expectedArgs) + " but it was never called."; 38 | } else { 39 | positiveMessage = "Expected spy " + this.actual.identity + " to have been called with initial arguments " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '') 40 | } 41 | return [positiveMessage, invertedMessage]; 42 | }; 43 | 44 | var actualInitial = _.map(this.actual.argsForCall, function (args) { 45 | return args.slice(0, expectedArgs.length); 46 | }); 47 | return this.env.contains_(actualInitial, expectedArgs); 48 | } 49 | 50 | }); 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/match-highlighter.js: -------------------------------------------------------------------------------- 1 | // Define match-highlighter commands. Depends on searchcursor.js 2 | // Use by attaching the following function call to the cursorActivity event: 3 | //myCodeMirror.matchHighlight(minChars); 4 | // And including a special span.CodeMirror-matchhighlight css class (also optionally a separate one for .CodeMirror-focused -- see demo matchhighlighter.html) 5 | 6 | (function() { 7 | var DEFAULT_MIN_CHARS = 2; 8 | 9 | function MatchHighlightState() { 10 | this.marked = []; 11 | } 12 | function getMatchHighlightState(cm) { 13 | return cm._matchHighlightState || (cm._matchHighlightState = new MatchHighlightState()); 14 | } 15 | 16 | function clearMarks(cm) { 17 | var state = getMatchHighlightState(cm); 18 | for (var i = 0; i < state.marked.length; ++i) 19 | state.marked[i].clear(); 20 | state.marked = []; 21 | } 22 | 23 | function markDocument(cm, className, minChars) { 24 | clearMarks(cm); 25 | minChars = (typeof minChars !== 'undefined' ? minChars : DEFAULT_MIN_CHARS); 26 | if (cm.somethingSelected() && cm.getSelection().replace(/^\s+|\s+$/g, "").length >= minChars) { 27 | var state = getMatchHighlightState(cm); 28 | var query = cm.getSelection(); 29 | cm.operation(function() { 30 | if (cm.lineCount() < 2000) { // This is too expensive on big documents. 31 | for (var cursor = cm.getSearchCursor(query); cursor.findNext();) { 32 | //Only apply matchhighlight to the matches other than the one actually selected 33 | if (cursor.from().line !== cm.getCursor(true).line || 34 | cursor.from().ch !== cm.getCursor(true).ch) 35 | state.marked.push(cm.markText(cursor.from(), cursor.to(), 36 | {className: className})); 37 | } 38 | } 39 | }); 40 | } 41 | } 42 | 43 | CodeMirror.defineExtension("matchHighlight", function(className, minChars) { 44 | markDocument(this, className, minChars); 45 | }); 46 | })(); 47 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/loadmode.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js"; 3 | 4 | var loading = {}; 5 | function splitCallback(cont, n) { 6 | var countDown = n; 7 | return function() { if (--countDown == 0) cont(); }; 8 | } 9 | function ensureDeps(mode, cont) { 10 | var deps = CodeMirror.modes[mode].dependencies; 11 | if (!deps) return cont(); 12 | var missing = []; 13 | for (var i = 0; i < deps.length; ++i) { 14 | if (!CodeMirror.modes.hasOwnProperty(deps[i])) 15 | missing.push(deps[i]); 16 | } 17 | if (!missing.length) return cont(); 18 | var split = splitCallback(cont, missing.length); 19 | for (var i = 0; i < missing.length; ++i) 20 | CodeMirror.requireMode(missing[i], split); 21 | } 22 | 23 | CodeMirror.requireMode = function(mode, cont) { 24 | if (typeof mode != "string") mode = mode.name; 25 | if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont); 26 | if (loading.hasOwnProperty(mode)) return loading[mode].push(cont); 27 | 28 | var script = document.createElement("script"); 29 | script.src = CodeMirror.modeURL.replace(/%N/g, mode); 30 | var others = document.getElementsByTagName("script")[0]; 31 | others.parentNode.insertBefore(script, others); 32 | var list = loading[mode] = [cont]; 33 | var count = 0, poll = setInterval(function() { 34 | if (++count > 100) return clearInterval(poll); 35 | if (CodeMirror.modes.hasOwnProperty(mode)) { 36 | clearInterval(poll); 37 | loading[mode] = null; 38 | ensureDeps(mode, function() { 39 | for (var i = 0; i < list.length; ++i) list[i](); 40 | }); 41 | } 42 | }, 200); 43 | }; 44 | 45 | CodeMirror.autoLoadMode = function(instance, mode) { 46 | if (!CodeMirror.modes.hasOwnProperty(mode)) 47 | CodeMirror.requireMode(mode, function() { 48 | instance.setOption("mode", instance.getOption("mode")); 49 | }); 50 | }; 51 | }()); 52 | -------------------------------------------------------------------------------- /spec/javascripts/support/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Mon Dec 30 2013 16:14:03 GMT+0100 (CET) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path, that will be used to resolve files and exclude 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | frameworks: ['jasmine'], 13 | 14 | 15 | // list of files / patterns to load in the browser 16 | files: [ 17 | '../../../public/javascripts/underscore.js', 18 | '../../../public/javascripts/backbone.js', 19 | '../../../src/backbone.picky.js', 20 | '../helpers/*.js', 21 | '../*.spec.js' 22 | ], 23 | 24 | 25 | // list of files to exclude 26 | exclude: [ 27 | 28 | ], 29 | 30 | 31 | // test results reporter to use 32 | // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' 33 | reporters: ['progress'], 34 | 35 | 36 | // web server port 37 | port: 9876, 38 | 39 | 40 | // enable / disable colors in the output (reporters and logs) 41 | colors: true, 42 | 43 | 44 | // level of logging 45 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 46 | logLevel: config.LOG_INFO, 47 | 48 | 49 | // enable / disable watching file and executing tests whenever any file changes 50 | autoWatch: false, 51 | 52 | 53 | // Start these browsers, currently available: 54 | // - Chrome 55 | // - ChromeCanary 56 | // - Firefox 57 | // - Opera (has to be installed with `npm install karma-opera-launcher`) 58 | // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`) 59 | // - PhantomJS 60 | // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`) 61 | // 62 | // ATTN Interactive debugging in PhpStorm/WebStorm doesn't work with PhantomJS. Use Firefox or Chrome instead. 63 | browsers: ['PhantomJS'], 64 | 65 | 66 | // If browser does not capture in given timeout [ms], kill it 67 | captureTimeout: 60000, 68 | 69 | 70 | // Continuous Integration mode 71 | // if true, it capture browsers, run tests and exit 72 | singleRun: false 73 | }); 74 | }; 75 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Backbone.Picky 2 | 3 | This is a dead repo, don't use it. 4 | 5 | I still keep it around because it contains a number of fixes and enhancements for the canonical [Backbone.Picky][], and those changes are referenced over there in issues and PRs. 6 | 7 | Unfortunately, though, Backbone.Picky does not appear to be maintained any more. As an alternative, you can use [Backbone.Select][], which is a fork of Backbone.Picky (maintained by me). Backbone.Select incorporates all the fixes and improvements you see here, and more. See the [compatibility notes][picky-compatibility] for a comparison of Backbone.Select and Backbone.Picky. 8 | 9 | You might also want to consider [Backbone.Cycle][], which layers additional functionality on top of Backbone.Select. 10 | 11 | 12 | ## Original MIT License 13 | 14 | Copyright (c) 2012 Derick Bailey, Muted Solutions, LLC 15 | 16 | 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: 17 | 18 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 19 | 20 | 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. 21 | 22 | [Backbone.Picky]: https://github.com/derickbailey/backbone.picky "Backbone.Picky" 23 | [Backbone.Select]: https://github.com/hashchange/backbone.select#readme "Backbone.Select" 24 | [Backbone.Cycle]: https://github.com/hashchange/backbone.cycle#readme "Backbone.Cycle" 25 | 26 | [picky-compatibility]: https://github.com/hashchange/backbone.select#compatibility-with-backbonepicky "Compatibility with Backbone.Picky" -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/overlay.js: -------------------------------------------------------------------------------- 1 | // Utility function that allows modes to be combined. The mode given 2 | // as the base argument takes care of most of the normal mode 3 | // functionality, but a second (typically simple) mode is used, which 4 | // can override the style of text. Both modes get to parse all of the 5 | // text, but when both assign a non-null style to a piece of code, the 6 | // overlay wins, unless the combine argument was true, in which case 7 | // the styles are combined. 8 | 9 | // overlayParser is the old, deprecated name 10 | CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, combine) { 11 | return { 12 | startState: function() { 13 | return { 14 | base: CodeMirror.startState(base), 15 | overlay: CodeMirror.startState(overlay), 16 | basePos: 0, baseCur: null, 17 | overlayPos: 0, overlayCur: null 18 | }; 19 | }, 20 | copyState: function(state) { 21 | return { 22 | base: CodeMirror.copyState(base, state.base), 23 | overlay: CodeMirror.copyState(overlay, state.overlay), 24 | basePos: state.basePos, baseCur: null, 25 | overlayPos: state.overlayPos, overlayCur: null 26 | }; 27 | }, 28 | 29 | token: function(stream, state) { 30 | if (stream.start == state.basePos) { 31 | state.baseCur = base.token(stream, state.base); 32 | state.basePos = stream.pos; 33 | } 34 | if (stream.start == state.overlayPos) { 35 | stream.pos = stream.start; 36 | state.overlayCur = overlay.token(stream, state.overlay); 37 | state.overlayPos = stream.pos; 38 | } 39 | stream.pos = Math.min(state.basePos, state.overlayPos); 40 | if (stream.eol()) state.basePos = state.overlayPos = 0; 41 | 42 | if (state.overlayCur == null) return state.baseCur; 43 | if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur; 44 | else return state.overlayCur; 45 | }, 46 | 47 | indent: base.indent && function(state, textAfter) { 48 | return base.indent(state.base, textAfter); 49 | }, 50 | electricChars: base.electricChars, 51 | 52 | innerMode: function(state) { return {state: state.base, mode: base}; }, 53 | 54 | blankLine: function(state) { 55 | if (base.blankLine) base.blankLine(state.base); 56 | if (overlay.blankLine) overlay.blankLine(state.overlay); 57 | } 58 | }; 59 | }; 60 | -------------------------------------------------------------------------------- /reports/assets/scripts/codemirror.markpopovertext.js: -------------------------------------------------------------------------------- 1 | /*global CodeMirror:false, $:false*/ 2 | 3 | (function(){ 4 | "use strict"; 5 | 6 | function makeid(num){ 7 | num = num || 5; 8 | var text = ""; 9 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 10 | 11 | for( var i=0; i < num; i++ ) 12 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 13 | 14 | return text; 15 | } 16 | 17 | CodeMirror.prototype.markPopoverText = function(lineObj, regex, className, gutter, message){ 18 | var re = new RegExp('(' + regex + ')', 'g'); 19 | var cursor = this.getSearchCursor(re, lineObj); 20 | 21 | var match, internalClass = 'plato-mark-' + makeid(10); 22 | while (match = cursor.findNext()) { 23 | if (cursor.to().line !== lineObj.line) break; 24 | this.markText( 25 | { line : lineObj.line, ch : cursor.from().ch }, 26 | { line : lineObj.line, ch : cursor.to().ch }, 27 | { 28 | className : 'plato-mark ' + internalClass + ' ' + (className || ''), 29 | startStyle : 'plato-mark-start', 30 | endStyle : 'plato-mark-end' 31 | } 32 | ); 33 | } 34 | 35 | if (gutter) { 36 | this.setGutterMarker(lineObj.line, gutter.gutterId, gutter.el); 37 | } 38 | 39 | // return a function to bind hover events, to be run after 40 | // the codemirror operations are executed 41 | return function(){ 42 | var markStart = $('.plato-mark-start.' + internalClass); 43 | var markSpans = $('.' + internalClass); 44 | 45 | if (message.type === 'popover') { 46 | 47 | var triggered = false; 48 | markSpans.add(gutter.el) 49 | .on('mouseenter touchstart',function(e){ 50 | e.preventDefault(); 51 | triggered = true; 52 | markSpans.addClass('active'); 53 | markStart.popover('show'); 54 | }) 55 | .on('mouseleave touchend',function(e){ 56 | e.preventDefault(); 57 | markSpans.removeClass('active'); 58 | triggered = false; 59 | setTimeout(function(){ 60 | if (!triggered) markStart.popover('hide'); 61 | },200); 62 | }); 63 | 64 | markStart.popover({ 65 | trigger : 'manual', 66 | content : message.content, 67 | html : true, 68 | title : message.title, 69 | placement : 'top' 70 | }); 71 | } else if (message.type === 'block') { 72 | this.addLineWidget(lineObj.line, $(message.content)[0]); 73 | } 74 | }; 75 | }; 76 | 77 | })(); 78 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/dialog.js: -------------------------------------------------------------------------------- 1 | // Open simple dialogs on top of an editor. Relies on dialog.css. 2 | 3 | (function() { 4 | function dialogDiv(cm, template, bottom) { 5 | var wrap = cm.getWrapperElement(); 6 | var dialog; 7 | dialog = wrap.appendChild(document.createElement("div")); 8 | if (bottom) { 9 | dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom"; 10 | } else { 11 | dialog.className = "CodeMirror-dialog CodeMirror-dialog-top"; 12 | } 13 | dialog.innerHTML = template; 14 | return dialog; 15 | } 16 | 17 | CodeMirror.defineExtension("openDialog", function(template, callback, options) { 18 | var dialog = dialogDiv(this, template, options && options.bottom); 19 | var closed = false, me = this; 20 | function close() { 21 | if (closed) return; 22 | closed = true; 23 | dialog.parentNode.removeChild(dialog); 24 | } 25 | var inp = dialog.getElementsByTagName("input")[0], button; 26 | if (inp) { 27 | CodeMirror.on(inp, "keydown", function(e) { 28 | if (e.keyCode == 13 || e.keyCode == 27) { 29 | CodeMirror.e_stop(e); 30 | close(); 31 | me.focus(); 32 | if (e.keyCode == 13) callback(inp.value); 33 | } 34 | }); 35 | inp.focus(); 36 | CodeMirror.on(inp, "blur", close); 37 | } else if (button = dialog.getElementsByTagName("button")[0]) { 38 | CodeMirror.on(button, "click", function() { 39 | close(); 40 | me.focus(); 41 | }); 42 | button.focus(); 43 | CodeMirror.on(button, "blur", close); 44 | } 45 | return close; 46 | }); 47 | 48 | CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) { 49 | var dialog = dialogDiv(this, template, options && options.bottom); 50 | var buttons = dialog.getElementsByTagName("button"); 51 | var closed = false, me = this, blurring = 1; 52 | function close() { 53 | if (closed) return; 54 | closed = true; 55 | dialog.parentNode.removeChild(dialog); 56 | me.focus(); 57 | } 58 | buttons[0].focus(); 59 | for (var i = 0; i < buttons.length; ++i) { 60 | var b = buttons[i]; 61 | (function(callback) { 62 | CodeMirror.on(b, "click", function(e) { 63 | CodeMirror.e_preventDefault(e); 64 | close(); 65 | if (callback) callback(me); 66 | }); 67 | })(callbacks[i]); 68 | CodeMirror.on(b, "blur", function() { 69 | --blurring; 70 | setTimeout(function() { if (blurring <= 0) close(); }, 200); 71 | }); 72 | CodeMirror.on(b, "focus", function() { ++blurring; }); 73 | } 74 | }); 75 | })(); 76 | -------------------------------------------------------------------------------- /reports/assets/scripts/plato-sortable-file-list.js: -------------------------------------------------------------------------------- 1 | /* global $:false, _:false */ 2 | /* jshint browser:true */ 3 | 4 | /* 5 | author: david linse 6 | version: 0.0.1 7 | 8 | A very first draft to add the ability to sort 9 | the "file-list" by the displayed 'numbers' for: 10 | 11 | + lint-errors 12 | + complexity 13 | + lines of code 14 | + estimated errors 15 | 16 | A group of buttons is added to the template above 17 | to trigger the update of the file-list. 18 | */ 19 | 20 | $(function sortable_file_list () { 21 | 22 | "use strict"; 23 | 24 | var file_list = $('ul.file-list'); 25 | 26 | var files = file_list.find('li'); 27 | 28 | // work-horse 29 | // @param: key The 'data-' to sort by 30 | // @return: descending sorted array of
  • elements 31 | // 32 | var _sortBy = function (key) { 33 | return _.sortBy(files, function (el) { 34 | return Number($(el).find('span[data-lint]').attr(key)) * -1; 35 | }); 36 | }; 37 | 38 | // sorter 39 | 40 | var _sortByLintErr = function _sortByLintErr () { 41 | return _sortBy('data-lint'); 42 | }; 43 | 44 | var _sortBySLOC = function _sortBySLOC () { 45 | return _sortBy('data-sloc'); 46 | }; 47 | 48 | var _sortByBugs = function _sortByBugs () { 49 | return _sortBy('data-bugs'); 50 | }; 51 | 52 | var _sortByComplexity = function _sortByComplexity () { 53 | return _sortBy('data-complexity'); 54 | }; 55 | 56 | // appends the 'list' of '
  • ' elements 57 | // to its parent '
      '. 58 | // @param: a list of '
    • '' elements 59 | // 60 | var _update_list = function _update_list (list) { 61 | file_list.append($(list)); 62 | }; 63 | 64 | // button event-handler 65 | 66 | var _byComplexity = function () { 67 | _update_list(_sortByComplexity()); 68 | }; 69 | 70 | var _byBugs = function () { 71 | _update_list(_sortByBugs()); 72 | }; 73 | 74 | var _bySLOC = function () { 75 | _update_list(_sortBySLOC()); 76 | }; 77 | 78 | var _byLint = function () { 79 | _update_list(_sortByLintErr()); 80 | }; 81 | 82 | // styling 83 | 84 | var _update_state = function _update_state (target) { 85 | 86 | var prev = $('button.on'); 87 | prev.removeClass('on'); 88 | 89 | var current = $(target); 90 | current.addClass('on'); 91 | }; 92 | 93 | // setup button events 94 | 95 | $('button#button-complexity').on('click', _byComplexity); 96 | $('button#button-bugs').on('click', _byBugs); 97 | $('button#button-sloc').on('click', _bySLOC); 98 | $('button#button-lint').on('click', _byLint); 99 | 100 | // styling update for buttons 101 | 102 | var all = $('button.btn'); 103 | all.on('click', function (evt) { 104 | _update_state(evt.target); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/matchbrackets.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; 3 | function findMatchingBracket(cm) { 4 | var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; 5 | var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; 6 | if (!match) return null; 7 | var forward = match.charAt(1) == ">", d = forward ? 1 : -1; 8 | var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).type; 9 | 10 | var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; 11 | function scan(line, lineNo, start) { 12 | if (!line.text) return; 13 | var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1; 14 | if (start != null) pos = start + d; 15 | for (; pos != end; pos += d) { 16 | var ch = line.text.charAt(pos); 17 | if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).type == style) { 18 | var match = matching[ch]; 19 | if (match.charAt(1) == ">" == forward) stack.push(ch); 20 | else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; 21 | else if (!stack.length) return {pos: pos, match: true}; 22 | } 23 | } 24 | } 25 | for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) { 26 | if (i == cur.line) found = scan(line, i, pos); 27 | else found = scan(cm.getLineHandle(i), i); 28 | if (found) break; 29 | } 30 | return {from: {line: cur.line, ch: pos}, to: found && {line: i, ch: found.pos}, match: found && found.match}; 31 | } 32 | 33 | function matchBrackets(cm, autoclear) { 34 | var found = findMatchingBracket(cm); 35 | if (!found) return; 36 | var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; 37 | var one = cm.markText(found.from, {line: found.from.line, ch: found.from.ch + 1}, 38 | {className: style}); 39 | var two = found.to && cm.markText(found.to, {line: found.to.line, ch: found.to.ch + 1}, 40 | {className: style}); 41 | var clear = function() { 42 | cm.operation(function() { one.clear(); two && two.clear(); }); 43 | }; 44 | if (autoclear) setTimeout(clear, 800); 45 | else return clear; 46 | } 47 | 48 | var currentlyHighlighted = null; 49 | function doMatchBrackets(cm) { 50 | cm.operation(function() { 51 | if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} 52 | if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false); 53 | }); 54 | } 55 | 56 | CodeMirror.defineOption("matchBrackets", false, function(cm, val) { 57 | if (val) cm.on("cursorActivity", doMatchBrackets); 58 | else cm.off("cursorActivity", doMatchBrackets); 59 | }); 60 | 61 | CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); 62 | CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);}); 63 | })(); 64 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt) { 3 | 4 | // Project configuration. 5 | grunt.initConfig({ 6 | pkg: grunt.file.readJSON('package.json'), 7 | meta: { 8 | version: '<%= pkg.version %>', 9 | banner: '// Backbone.Picky, v<%= meta.version %>\n' + 10 | '// Copyright (c)<%= grunt.template.today("yyyy") %> Derick Bailey, Muted Solutions, LLC.\n' + 11 | '// Distributed under MIT license\n' + 12 | '// http://github.com/derickbailey/backbone.picky\n' + 13 | '\n' 14 | }, 15 | 16 | preprocess: { 17 | // Currently works as a copy 18 | build: { 19 | files: { 20 | 'lib/backbone.picky.js' : 'src/backbone.picky.js' 21 | } 22 | }, 23 | amd: { 24 | files: { 25 | 'lib/amd/backbone.picky.js' : 'src/amd.js' 26 | } 27 | } 28 | }, 29 | 30 | concat: { 31 | options: { 32 | banner: "<%= meta.banner %>" 33 | }, 34 | build: { 35 | src: 'lib/backbone.picky.js', 36 | dest: 'lib/backbone.picky.js' 37 | }, 38 | amd_banner: { 39 | src: 'lib/amd/backbone.picky.js', 40 | dest: 'lib/amd/backbone.picky.js' 41 | } 42 | }, 43 | 44 | uglify : { 45 | options: { 46 | banner: "<%= meta.banner %>" 47 | }, 48 | amd : { 49 | src : 'lib/amd/backbone.picky.js', 50 | dest : 'lib/amd/backbone.picky.min.js', 51 | }, 52 | core : { 53 | src : 'lib/backbone.picky.js', 54 | dest : 'lib/backbone.picky.min.js', 55 | options : { 56 | sourceMap : 'lib/backbone.picky.map', 57 | sourceMappingURL : 'backbone.picky.map', 58 | sourceMapPrefix : 1 59 | } 60 | } 61 | }, 62 | 63 | jasmine : { 64 | options : { 65 | helpers : 'spec/javascripts/helpers/*.js', 66 | specs : 'spec/javascripts/**/*.spec.js', 67 | vendor : [ 68 | 'public/javascripts/underscore.js', 69 | 'public/javascripts/backbone.js' 70 | ], 71 | }, 72 | coverage : { 73 | src : 'src/backbone.picky.js', 74 | options : { 75 | template : require('grunt-template-jasmine-istanbul'), 76 | templateOptions: { 77 | coverage: 'reports/coverage.json', 78 | report: 'reports/coverage' 79 | } 80 | } 81 | } 82 | }, 83 | 84 | jshint: { 85 | options: { 86 | jshintrc : '.jshintrc' 87 | }, 88 | picky : 'src/backbone.picky.js' 89 | }, 90 | 91 | plato: { 92 | picky : { 93 | src : 'src/*.js', 94 | dest : 'reports', 95 | options : { 96 | jshint : grunt.file.readJSON('.jshintrc') 97 | } 98 | } 99 | } 100 | 101 | }); 102 | 103 | grunt.loadNpmTasks('grunt-plato'); 104 | grunt.loadNpmTasks('grunt-preprocess'); 105 | grunt.loadNpmTasks('grunt-contrib-concat'); 106 | grunt.loadNpmTasks('grunt-contrib-jshint'); 107 | grunt.loadNpmTasks('grunt-contrib-uglify'); 108 | grunt.loadNpmTasks('grunt-contrib-jasmine'); 109 | 110 | grunt.registerTask('test', ['jshint', 'jasmine']); 111 | 112 | // Default task. 113 | grunt.registerTask('default', ['jshint', 'jasmine', 'preprocess', 'concat', 'uglify']); 114 | 115 | }; 116 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/runmode-standalone.js: -------------------------------------------------------------------------------- 1 | /* Just enough of CodeMirror to run runMode under node.js */ 2 | 3 | function splitLines(string){ return string.split(/\r?\n|\r/); }; 4 | 5 | function StringStream(string) { 6 | this.pos = this.start = 0; 7 | this.string = string; 8 | } 9 | StringStream.prototype = { 10 | eol: function() {return this.pos >= this.string.length;}, 11 | sol: function() {return this.pos == 0;}, 12 | peek: function() {return this.string.charAt(this.pos) || null;}, 13 | next: function() { 14 | if (this.pos < this.string.length) 15 | return this.string.charAt(this.pos++); 16 | }, 17 | eat: function(match) { 18 | var ch = this.string.charAt(this.pos); 19 | if (typeof match == "string") var ok = ch == match; 20 | else var ok = ch && (match.test ? match.test(ch) : match(ch)); 21 | if (ok) {++this.pos; return ch;} 22 | }, 23 | eatWhile: function(match) { 24 | var start = this.pos; 25 | while (this.eat(match)){} 26 | return this.pos > start; 27 | }, 28 | eatSpace: function() { 29 | var start = this.pos; 30 | while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; 31 | return this.pos > start; 32 | }, 33 | skipToEnd: function() {this.pos = this.string.length;}, 34 | skipTo: function(ch) { 35 | var found = this.string.indexOf(ch, this.pos); 36 | if (found > -1) {this.pos = found; return true;} 37 | }, 38 | backUp: function(n) {this.pos -= n;}, 39 | column: function() {return this.start;}, 40 | indentation: function() {return 0;}, 41 | match: function(pattern, consume, caseInsensitive) { 42 | if (typeof pattern == "string") { 43 | function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} 44 | if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { 45 | if (consume !== false) this.pos += pattern.length; 46 | return true; 47 | } 48 | } 49 | else { 50 | var match = this.string.slice(this.pos).match(pattern); 51 | if (match && consume !== false) this.pos += match[0].length; 52 | return match; 53 | } 54 | }, 55 | current: function(){return this.string.slice(this.start, this.pos);} 56 | }; 57 | exports.StringStream = StringStream; 58 | 59 | exports.startState = function(mode, a1, a2) { 60 | return mode.startState ? mode.startState(a1, a2) : true; 61 | }; 62 | 63 | var modes = exports.modes = {}, mimeModes = exports.mimeModes = {}; 64 | exports.defineMode = function(name, mode) { modes[name] = mode; }; 65 | exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; 66 | exports.getMode = function(options, spec) { 67 | if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) 68 | spec = mimeModes[spec]; 69 | if (typeof spec == "string") 70 | var mname = spec, config = {}; 71 | else if (spec != null) 72 | var mname = spec.name, config = spec; 73 | var mfactory = modes[mname]; 74 | if (!mfactory) throw new Error("Unknown mode: " + spec); 75 | return mfactory(options, config || {}); 76 | }; 77 | 78 | exports.runMode = function(string, modespec, callback) { 79 | var mode = exports.getMode({indentUnit: 2}, modespec); 80 | var lines = splitLines(string), state = exports.startState(mode); 81 | for (var i = 0, e = lines.length; i < e; ++i) { 82 | if (i) callback("\n"); 83 | var stream = new exports.StringStream(lines[i]); 84 | while (!stream.eol()) { 85 | var style = mode.token(stream, state); 86 | callback(stream.current(), style, i, stream.start); 87 | stream.start = stream.pos; 88 | } 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /reports/assets/css/plato-overview.css: -------------------------------------------------------------------------------- 1 | .chart { 2 | margin: 0 auto; 3 | height: 200px; 4 | } 5 | 6 | .overview .chart { 7 | height: 250px; 8 | } 9 | 10 | .historical .chart { 11 | height:200px; 12 | } 13 | 14 | .chart rect { 15 | cursor:pointer; 16 | } 17 | 18 | .file-list li { 19 | border-bottom:1px solid #ccc; 20 | padding-bottom:10px; 21 | padding-top:10px; 22 | } 23 | 24 | .file-list li:nth-child(odd) { 25 | background-color: hsl(0, 0%, 98%); 26 | } 27 | 28 | .fade-left { 29 | background: -moz-linear-gradient(left, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%); /* FF3.6+ */ 30 | background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */ 31 | background: -webkit-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ */ 32 | background: -o-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Opera 11.10+ */ 33 | background: -ms-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* IE10+ */ 34 | background: linear-gradient(to right, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* W3C */ 35 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#00ffffff',GradientType=1 ); /* IE6-9 */ 36 | } 37 | 38 | .file-list li:nth-child(odd) .fade-left { 39 | background: -moz-linear-gradient(left, rgba(249,249,249,1) 0%, rgba(255,255,255,0) 100%); /* FF3.6+ */ 40 | background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(249,249,249,1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */ 41 | background: -webkit-linear-gradient(left, rgba(249,249,249,1) 0%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ */ 42 | background: -o-linear-gradient(left, rgba(249,249,249,1) 0%,rgba(255,255,255,0) 100%); /* Opera 11.10+ */ 43 | background: -ms-linear-gradient(left, rgba(249,249,249,1) 0%,rgba(255,255,255,0) 100%); /* IE10+ */ 44 | background: linear-gradient(to right, rgba(249,249,249,1) 0%,rgba(255,255,255,0) 100%); /* W3C */ 45 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f9f9f9', endColorstr='#00ffffff',GradientType=1 ); /* IE6-9 */ 46 | } 47 | 48 | .fadeout { 49 | position: absolute; 50 | height: 50px; 51 | z-index: 10; 52 | float: left; 53 | width:70px 54 | } 55 | 56 | .file { 57 | white-space: nowrap; 58 | } 59 | 60 | 61 | .file-link { 62 | text-align: right; 63 | direction: rtl; 64 | overflow: hidden; 65 | height:40px; 66 | font-size:20px; 67 | color: #334B6D; 68 | display:block; 69 | padding:12px 12px 12px 0; 70 | text-decoration: underline; 71 | } 72 | 73 | .file-link:hover { 74 | color: #3B71B1; 75 | } 76 | 77 | .file-chart label { 78 | width: 75px; 79 | text-align: right; 80 | margin-right: 10px; 81 | } 82 | 83 | .file-chart .chart-value { 84 | margin-left: 3px; 85 | font-size:11px; 86 | } 87 | 88 | .horizontal-bar { 89 | display:inline-block; 90 | height:8px; 91 | border-radius: 0 4px 4px 0; 92 | } 93 | 94 | .threshold-0 .horizontal-bar { 95 | background-color: #01939A; 96 | } 97 | .threshold-1 .horizontal-bar { 98 | background-color: #FFAB00; 99 | } 100 | .threshold-2 .horizontal-bar { 101 | background-color: #FF0700; 102 | } 103 | 104 | 105 | @media (max-width: 767px) { 106 | .file-link { 107 | text-align: center; 108 | } 109 | } 110 | 111 | .complexity, .sloc, .bugs, .lint { 112 | font-weight: normal; 113 | } 114 | 115 | .on { /* marking 'sorting buttons' active */ 116 | font-weight: bold; 117 | } 118 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/multiplex.js: -------------------------------------------------------------------------------- 1 | CodeMirror.multiplexingMode = function(outer /*, others */) { 2 | // Others should be {open, close, mode [, delimStyle]} objects 3 | var others = Array.prototype.slice.call(arguments, 1); 4 | var n_others = others.length; 5 | 6 | function indexOf(string, pattern, from) { 7 | if (typeof pattern == "string") return string.indexOf(pattern, from); 8 | var m = pattern.exec(from ? string.slice(from) : string); 9 | return m ? m.index + from : -1; 10 | } 11 | 12 | return { 13 | startState: function() { 14 | return { 15 | outer: CodeMirror.startState(outer), 16 | innerActive: null, 17 | inner: null 18 | }; 19 | }, 20 | 21 | copyState: function(state) { 22 | return { 23 | outer: CodeMirror.copyState(outer, state.outer), 24 | innerActive: state.innerActive, 25 | inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner) 26 | }; 27 | }, 28 | 29 | token: function(stream, state) { 30 | if (!state.innerActive) { 31 | var cutOff = Infinity, oldContent = stream.string; 32 | for (var i = 0; i < n_others; ++i) { 33 | var other = others[i]; 34 | var found = indexOf(oldContent, other.open, stream.pos); 35 | if (found == stream.pos) { 36 | stream.match(other.open); 37 | state.innerActive = other; 38 | state.inner = CodeMirror.startState(other.mode, outer.indent ? outer.indent(state.outer, "") : 0); 39 | return other.delimStyle; 40 | } else if (found != -1 && found < cutOff) { 41 | cutOff = found; 42 | } 43 | } 44 | if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff); 45 | var outerToken = outer.token(stream, state.outer); 46 | if (cutOff != Infinity) stream.string = oldContent; 47 | return outerToken; 48 | } else { 49 | var curInner = state.innerActive, oldContent = stream.string; 50 | var found = indexOf(oldContent, curInner.close, stream.pos); 51 | if (found == stream.pos) { 52 | stream.match(curInner.close); 53 | state.innerActive = state.inner = null; 54 | return curInner.delimStyle; 55 | } 56 | if (found > -1) stream.string = oldContent.slice(0, found); 57 | var innerToken = curInner.mode.token(stream, state.inner); 58 | if (found > -1) stream.string = oldContent; 59 | var cur = stream.current(), found = cur.indexOf(curInner.close); 60 | if (found > -1) stream.backUp(cur.length - found); 61 | return innerToken; 62 | } 63 | }, 64 | 65 | indent: function(state, textAfter) { 66 | var mode = state.innerActive ? state.innerActive.mode : outer; 67 | if (!mode.indent) return CodeMirror.Pass; 68 | return mode.indent(state.innerActive ? state.inner : state.outer, textAfter); 69 | }, 70 | 71 | blankLine: function(state) { 72 | var mode = state.innerActive ? state.innerActive.mode : outer; 73 | if (mode.blankLine) { 74 | mode.blankLine(state.innerActive ? state.inner : state.outer); 75 | } 76 | if (!state.innerActive) { 77 | for (var i = 0; i < n_others; ++i) { 78 | var other = others[i]; 79 | if (other.open === "\n") { 80 | state.innerActive = other; 81 | state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0); 82 | } 83 | } 84 | } else if (mode.close === "\n") { 85 | state.innerActive = state.inner = null; 86 | } 87 | }, 88 | 89 | electricChars: outer.electricChars, 90 | 91 | innerMode: function(state) { 92 | return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer}; 93 | } 94 | }; 95 | }; 96 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/xml-hint.js: -------------------------------------------------------------------------------- 1 | 2 | (function() { 3 | 4 | CodeMirror.xmlHints = []; 5 | 6 | CodeMirror.xmlHint = function(cm, simbol) { 7 | 8 | if(simbol.length > 0) { 9 | var cursor = cm.getCursor(); 10 | cm.replaceSelection(simbol); 11 | cursor = {line: cursor.line, ch: cursor.ch + 1}; 12 | cm.setCursor(cursor); 13 | } 14 | 15 | CodeMirror.simpleHint(cm, getHint); 16 | }; 17 | 18 | var getHint = function(cm) { 19 | 20 | var cursor = cm.getCursor(); 21 | 22 | if (cursor.ch > 0) { 23 | 24 | var text = cm.getRange({line: 0, ch: 0}, cursor); 25 | var typed = ''; 26 | var simbol = ''; 27 | for(var i = text.length - 1; i >= 0; i--) { 28 | if(text[i] == ' ' || text[i] == '<') { 29 | simbol = text[i]; 30 | break; 31 | } 32 | else { 33 | typed = text[i] + typed; 34 | } 35 | } 36 | 37 | text = text.slice(0, text.length - typed.length); 38 | 39 | var path = getActiveElement(text) + simbol; 40 | var hints = CodeMirror.xmlHints[path]; 41 | 42 | if(typeof hints === 'undefined') 43 | hints = ['']; 44 | else { 45 | hints = hints.slice(0); 46 | for (var i = hints.length - 1; i >= 0; i--) { 47 | if(hints[i].indexOf(typed) != 0) 48 | hints.splice(i, 1); 49 | } 50 | } 51 | 52 | return { 53 | list: hints, 54 | from: { line: cursor.line, ch: cursor.ch - typed.length }, 55 | to: cursor 56 | }; 57 | }; 58 | }; 59 | 60 | var getActiveElement = function(text) { 61 | 62 | var element = ''; 63 | 64 | if(text.length >= 0) { 65 | 66 | var regex = new RegExp('<([^!?][^\\s/>]*).*?>', 'g'); 67 | 68 | var matches = []; 69 | var match; 70 | while ((match = regex.exec(text)) != null) { 71 | matches.push({ 72 | tag: match[1], 73 | selfclose: (match[0].slice(match[0].length - 2) === '/>') 74 | }); 75 | } 76 | 77 | for (var i = matches.length - 1, skip = 0; i >= 0; i--) { 78 | 79 | var item = matches[i]; 80 | 81 | if (item.tag[0] == '/') 82 | { 83 | skip++; 84 | } 85 | else if (item.selfclose == false) 86 | { 87 | if (skip > 0) 88 | { 89 | skip--; 90 | } 91 | else 92 | { 93 | element = '<' + item.tag + '>' + element; 94 | } 95 | } 96 | } 97 | 98 | element += getOpenTag(text); 99 | } 100 | 101 | return element; 102 | }; 103 | 104 | var getOpenTag = function(text) { 105 | 106 | var open = text.lastIndexOf('<'); 107 | var close = text.lastIndexOf('>'); 108 | 109 | if (close < open) 110 | { 111 | text = text.slice(open); 112 | 113 | if(text != '<') { 114 | 115 | var space = text.indexOf(' '); 116 | if(space < 0) 117 | space = text.indexOf('\t'); 118 | if(space < 0) 119 | space = text.indexOf('\n'); 120 | 121 | if (space < 0) 122 | space = text.length; 123 | 124 | return text.slice(0, space); 125 | } 126 | } 127 | 128 | return ''; 129 | }; 130 | 131 | })(); 132 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/closetag.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tag-closer extension for CodeMirror. 3 | * 4 | * This extension adds an "autoCloseTags" option that can be set to 5 | * either true to get the default behavior, or an object to further 6 | * configure its behavior. 7 | * 8 | * These are supported options: 9 | * 10 | * `whenClosing` (default true) 11 | * Whether to autoclose when the '/' of a closing tag is typed. 12 | * `whenOpening` (default true) 13 | * Whether to autoclose the tag when the final '>' of an opening 14 | * tag is typed. 15 | * `dontCloseTags` (default is empty tags for HTML, none for XML) 16 | * An array of tag names that should not be autoclosed. 17 | * `indentTags` (default is block tags for HTML, none for XML) 18 | * An array of tag names that should, when opened, cause a 19 | * blank line to be added inside the tag, and the blank line and 20 | * closing line to be indented. 21 | * 22 | * See demos/closetag.html for a usage example. 23 | */ 24 | 25 | (function() { 26 | CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) { 27 | if (val && (old == CodeMirror.Init || !old)) { 28 | var map = {name: "autoCloseTags"}; 29 | if (typeof val != "object" || val.whenClosing) 30 | map["'/'"] = function(cm) { autoCloseTag(cm, '/'); }; 31 | if (typeof val != "object" || val.whenOpening) 32 | map["'>'"] = function(cm) { autoCloseTag(cm, '>'); }; 33 | cm.addKeyMap(map); 34 | } else if (!val && (old != CodeMirror.Init && old)) { 35 | cm.removeKeyMap("autoCloseTags"); 36 | } 37 | }); 38 | 39 | var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", 40 | "source", "track", "wbr"]; 41 | var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4", 42 | "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"]; 43 | 44 | function autoCloseTag(cm, ch) { 45 | var pos = cm.getCursor(), tok = cm.getTokenAt(pos); 46 | var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; 47 | if (inner.mode.name != "xml") throw CodeMirror.Pass; 48 | 49 | var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; 50 | var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); 51 | var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); 52 | 53 | if (ch == ">" && state.tagName) { 54 | var tagName = state.tagName; 55 | if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); 56 | var lowerTagName = tagName.toLowerCase(); 57 | // Don't process the '>' at the end of an end-tag or self-closing tag 58 | if (tok.type == "tag" && state.type == "closeTag" || 59 | /\/\s*$/.test(tok.string) || 60 | dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) 61 | throw CodeMirror.Pass; 62 | 63 | var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; 64 | cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", 65 | doIndent ? {line: pos.line + 1, ch: 0} : {line: pos.line, ch: pos.ch + 1}); 66 | if (doIndent) { 67 | cm.indentLine(pos.line + 1); 68 | cm.indentLine(pos.line + 2); 69 | } 70 | return; 71 | } else if (ch == "/" && tok.type == "tag" && tok.string == "<") { 72 | var tagName = state.context && state.context.tagName; 73 | if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); 74 | return; 75 | } 76 | throw CodeMirror.Pass; 77 | } 78 | 79 | function indexOf(collection, elt) { 80 | if (collection.indexOf) return collection.indexOf(elt); 81 | for (var i = 0, e = collection.length; i < e; ++i) 82 | if (collection[i] == elt) return i; 83 | return -1; 84 | } 85 | })(); 86 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/bootstrap-popover.js: -------------------------------------------------------------------------------- 1 | /* ======================================================================== 2 | * Bootstrap: popover.js v3.0.0 3 | * http://twbs.github.com/bootstrap/javascript.html#popovers 4 | * ======================================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ======================================================================== */ 19 | 20 | 21 | +function ($) { "use strict"; 22 | 23 | // POPOVER PUBLIC CLASS DEFINITION 24 | // =============================== 25 | 26 | var Popover = function (element, options) { 27 | this.init('popover', element, options) 28 | } 29 | 30 | if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') 31 | 32 | Popover.DEFAULTS = $.extend({} , $.fn.tooltip.Constructor.DEFAULTS, { 33 | placement: 'right' 34 | , trigger: 'click' 35 | , content: '' 36 | , template: '

      ' 37 | }) 38 | 39 | 40 | // NOTE: POPOVER EXTENDS tooltip.js 41 | // ================================ 42 | 43 | Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) 44 | 45 | Popover.prototype.constructor = Popover 46 | 47 | Popover.prototype.getDefaults = function () { 48 | return Popover.DEFAULTS 49 | } 50 | 51 | Popover.prototype.setContent = function () { 52 | var $tip = this.tip() 53 | var title = this.getTitle() 54 | var content = this.getContent() 55 | 56 | $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) 57 | $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content) 58 | 59 | $tip.removeClass('fade top bottom left right in') 60 | 61 | // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do 62 | // this manually by checking the contents. 63 | if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() 64 | } 65 | 66 | Popover.prototype.hasContent = function () { 67 | return this.getTitle() || this.getContent() 68 | } 69 | 70 | Popover.prototype.getContent = function () { 71 | var $e = this.$element 72 | var o = this.options 73 | 74 | return $e.attr('data-content') 75 | || (typeof o.content == 'function' ? 76 | o.content.call($e[0]) : 77 | o.content) 78 | } 79 | 80 | Popover.prototype.arrow = function () { 81 | return this.$arrow = this.$arrow || this.tip().find('.arrow') 82 | } 83 | 84 | Popover.prototype.tip = function () { 85 | if (!this.$tip) this.$tip = $(this.options.template) 86 | return this.$tip 87 | } 88 | 89 | 90 | // POPOVER PLUGIN DEFINITION 91 | // ========================= 92 | 93 | var old = $.fn.popover 94 | 95 | $.fn.popover = function (option) { 96 | return this.each(function () { 97 | var $this = $(this) 98 | var data = $this.data('bs.popover') 99 | var options = typeof option == 'object' && option 100 | 101 | if (!data) $this.data('bs.popover', (data = new Popover(this, options))) 102 | if (typeof option == 'string') data[option]() 103 | }) 104 | } 105 | 106 | $.fn.popover.Constructor = Popover 107 | 108 | 109 | // POPOVER NO CONFLICT 110 | // =================== 111 | 112 | $.fn.popover.noConflict = function () { 113 | $.fn.popover = old 114 | return this 115 | } 116 | 117 | }(window.jQuery); 118 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/formatting.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | CodeMirror.extendMode("css", { 4 | commentStart: "/*", 5 | commentEnd: "*/", 6 | newlineAfterToken: function(_type, content) { 7 | return /^[;{}]$/.test(content); 8 | } 9 | }); 10 | 11 | CodeMirror.extendMode("javascript", { 12 | commentStart: "/*", 13 | commentEnd: "*/", 14 | // FIXME semicolons inside of for 15 | newlineAfterToken: function(_type, content, textAfter, state) { 16 | if (this.jsonMode) { 17 | return /^[\[,{]$/.test(content) || /^}/.test(textAfter); 18 | } else { 19 | if (content == ";" && state.lexical && state.lexical.type == ")") return false; 20 | return /^[;{}]$/.test(content) && !/^;/.test(textAfter); 21 | } 22 | } 23 | }); 24 | 25 | CodeMirror.extendMode("xml", { 26 | commentStart: "", 28 | newlineAfterToken: function(type, content, textAfter) { 29 | return type == "tag" && />$/.test(content) || /^ -1 && endIndex > -1 && endIndex > startIndex) { 47 | // Take string till comment start 48 | selText = selText.substr(0, startIndex) 49 | // From comment start till comment end 50 | + selText.substring(startIndex + curMode.commentStart.length, endIndex) 51 | // From comment end till string end 52 | + selText.substr(endIndex + curMode.commentEnd.length); 53 | } 54 | cm.replaceRange(selText, from, to); 55 | } 56 | }); 57 | }); 58 | 59 | // Applies automatic mode-aware indentation to the specified range 60 | CodeMirror.defineExtension("autoIndentRange", function (from, to) { 61 | var cmInstance = this; 62 | this.operation(function () { 63 | for (var i = from.line; i <= to.line; i++) { 64 | cmInstance.indentLine(i, "smart"); 65 | } 66 | }); 67 | }); 68 | 69 | // Applies automatic formatting to the specified range 70 | CodeMirror.defineExtension("autoFormatRange", function (from, to) { 71 | var cm = this; 72 | var outer = cm.getMode(), text = cm.getRange(from, to).split("\n"); 73 | var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state); 74 | var tabSize = cm.getOption("tabSize"); 75 | 76 | var out = "", lines = 0, atSol = from.ch == 0; 77 | function newline() { 78 | out += "\n"; 79 | atSol = true; 80 | ++lines; 81 | } 82 | 83 | for (var i = 0; i < text.length; ++i) { 84 | var stream = new CodeMirror.StringStream(text[i], tabSize); 85 | while (!stream.eol()) { 86 | var inner = CodeMirror.innerMode(outer, state); 87 | var style = outer.token(stream, state), cur = stream.current(); 88 | stream.start = stream.pos; 89 | if (!atSol || /\S/.test(cur)) { 90 | out += cur; 91 | atSol = false; 92 | } 93 | if (!atSol && inner.mode.newlineAfterToken && 94 | inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i+1] || "", inner.state)) 95 | newline(); 96 | } 97 | if (!stream.pos && outer.blankLine) outer.blankLine(state); 98 | if (!atSol) newline(); 99 | } 100 | 101 | cm.operation(function () { 102 | cm.replaceRange(out, from, to); 103 | for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur) 104 | cm.indentLine(cur, "smart"); 105 | cm.setSelection(from, cm.getCursor(false)); 106 | }); 107 | }); 108 | })(); 109 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/simple-hint.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | CodeMirror.simpleHint = function(editor, getHints, givenOptions) { 3 | // Determine effective options based on given values and defaults. 4 | var options = {}, defaults = CodeMirror.simpleHint.defaults; 5 | for (var opt in defaults) 6 | if (defaults.hasOwnProperty(opt)) 7 | options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; 8 | 9 | function collectHints(previousToken) { 10 | // We want a single cursor position. 11 | if (editor.somethingSelected()) return; 12 | 13 | var tempToken = editor.getTokenAt(editor.getCursor()); 14 | 15 | // Don't show completions if token has changed and the option is set. 16 | if (options.closeOnTokenChange && previousToken != null && 17 | (tempToken.start != previousToken.start || tempToken.type != previousToken.type)) { 18 | return; 19 | } 20 | 21 | var result = getHints(editor, givenOptions); 22 | if (!result || !result.list.length) return; 23 | var completions = result.list; 24 | function insert(str) { 25 | editor.replaceRange(str, result.from, result.to); 26 | } 27 | // When there is only one completion, use it directly. 28 | if (options.completeSingle && completions.length == 1) { 29 | insert(completions[0]); 30 | return true; 31 | } 32 | 33 | // Build the select widget 34 | var complete = document.createElement("div"); 35 | complete.className = "CodeMirror-completions"; 36 | var sel = complete.appendChild(document.createElement("select")); 37 | // Opera doesn't move the selection when pressing up/down in a 38 | // multi-select, but it does properly support the size property on 39 | // single-selects, so no multi-select is necessary. 40 | if (!window.opera) sel.multiple = true; 41 | for (var i = 0; i < completions.length; ++i) { 42 | var opt = sel.appendChild(document.createElement("option")); 43 | opt.appendChild(document.createTextNode(completions[i])); 44 | } 45 | sel.firstChild.selected = true; 46 | sel.size = Math.min(10, completions.length); 47 | var pos = editor.cursorCoords(options.alignWithWord ? result.from : null); 48 | complete.style.left = pos.left + "px"; 49 | complete.style.top = pos.bottom + "px"; 50 | document.body.appendChild(complete); 51 | // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. 52 | var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); 53 | if(winW - pos.left < sel.clientWidth) 54 | complete.style.left = (pos.left - sel.clientWidth) + "px"; 55 | // Hack to hide the scrollbar. 56 | if (completions.length <= 10) 57 | complete.style.width = (sel.clientWidth - 1) + "px"; 58 | 59 | var done = false; 60 | function close() { 61 | if (done) return; 62 | done = true; 63 | complete.parentNode.removeChild(complete); 64 | } 65 | function pick() { 66 | insert(completions[sel.selectedIndex]); 67 | close(); 68 | setTimeout(function(){editor.focus();}, 50); 69 | } 70 | CodeMirror.on(sel, "blur", close); 71 | CodeMirror.on(sel, "keydown", function(event) { 72 | var code = event.keyCode; 73 | // Enter 74 | if (code == 13) {CodeMirror.e_stop(event); pick();} 75 | // Escape 76 | else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();} 77 | else if (code != 38 && code != 40 && code != 33 && code != 34 && !CodeMirror.isModifierKey(event)) { 78 | close(); editor.focus(); 79 | // Pass the event to the CodeMirror instance so that it can handle things like backspace properly. 80 | editor.triggerOnKeyDown(event); 81 | // Don't show completions if the code is backspace and the option is set. 82 | if (!options.closeOnBackspace || code != 8) { 83 | setTimeout(function(){collectHints(tempToken);}, 50); 84 | } 85 | } 86 | }); 87 | CodeMirror.on(sel, "dblclick", pick); 88 | 89 | sel.focus(); 90 | // Opera sometimes ignores focusing a freshly created node 91 | if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100); 92 | return true; 93 | } 94 | return collectHints(); 95 | }; 96 | CodeMirror.simpleHint.defaults = { 97 | closeOnBackspace: true, 98 | closeOnTokenChange: false, 99 | completeSingle: true, 100 | alignWithWord: true 101 | }; 102 | })(); 103 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/searchcursor.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | function SearchCursor(cm, query, pos, caseFold) { 3 | this.atOccurrence = false; this.cm = cm; 4 | if (caseFold == null && typeof query == "string") caseFold = false; 5 | 6 | pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0}; 7 | this.pos = {from: pos, to: pos}; 8 | 9 | // The matches method is filled in based on the type of query. 10 | // It takes a position and a direction, and returns an object 11 | // describing the next occurrence of the query, or null if no 12 | // more matches were found. 13 | if (typeof query != "string") { // Regexp match 14 | if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g"); 15 | this.matches = function(reverse, pos) { 16 | if (reverse) { 17 | query.lastIndex = 0; 18 | var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0; 19 | while (match) { 20 | start += match.index + 1; 21 | line = line.slice(start); 22 | query.lastIndex = 0; 23 | var newmatch = query.exec(line); 24 | if (newmatch) match = newmatch; 25 | else break; 26 | } 27 | start--; 28 | } else { 29 | query.lastIndex = pos.ch; 30 | var line = cm.getLine(pos.line), match = query.exec(line), 31 | start = match && match.index; 32 | } 33 | if (match) 34 | return {from: {line: pos.line, ch: start}, 35 | to: {line: pos.line, ch: start + match[0].length}, 36 | match: match}; 37 | }; 38 | } else { // String query 39 | if (caseFold) query = query.toLowerCase(); 40 | var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; 41 | var target = query.split("\n"); 42 | // Different methods for single-line and multi-line queries 43 | if (target.length == 1) 44 | this.matches = function(reverse, pos) { 45 | var line = fold(cm.getLine(pos.line)), len = query.length, match; 46 | if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) 47 | : (match = line.indexOf(query, pos.ch)) != -1) 48 | return {from: {line: pos.line, ch: match}, 49 | to: {line: pos.line, ch: match + len}}; 50 | }; 51 | else 52 | this.matches = function(reverse, pos) { 53 | var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln)); 54 | var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); 55 | if (reverse ? offsetA >= pos.ch || offsetA != match.length 56 | : offsetA <= pos.ch || offsetA != line.length - match.length) 57 | return; 58 | for (;;) { 59 | if (reverse ? !ln : ln == cm.lineCount() - 1) return; 60 | line = fold(cm.getLine(ln += reverse ? -1 : 1)); 61 | match = target[reverse ? --idx : ++idx]; 62 | if (idx > 0 && idx < target.length - 1) { 63 | if (line != match) return; 64 | else continue; 65 | } 66 | var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); 67 | if (reverse ? offsetB != line.length - match.length : offsetB != match.length) 68 | return; 69 | var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB}; 70 | return {from: reverse ? end : start, to: reverse ? start : end}; 71 | } 72 | }; 73 | } 74 | } 75 | 76 | SearchCursor.prototype = { 77 | findNext: function() {return this.find(false);}, 78 | findPrevious: function() {return this.find(true);}, 79 | 80 | find: function(reverse) { 81 | var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to); 82 | function savePosAndFail(line) { 83 | var pos = {line: line, ch: 0}; 84 | self.pos = {from: pos, to: pos}; 85 | self.atOccurrence = false; 86 | return false; 87 | } 88 | 89 | for (;;) { 90 | if (this.pos = this.matches(reverse, pos)) { 91 | this.atOccurrence = true; 92 | return this.pos.match || true; 93 | } 94 | if (reverse) { 95 | if (!pos.line) return savePosAndFail(0); 96 | pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length}; 97 | } 98 | else { 99 | var maxLine = this.cm.lineCount(); 100 | if (pos.line == maxLine - 1) return savePosAndFail(maxLine); 101 | pos = {line: pos.line+1, ch: 0}; 102 | } 103 | } 104 | }, 105 | 106 | from: function() {if (this.atOccurrence) return this.pos.from;}, 107 | to: function() {if (this.atOccurrence) return this.pos.to;}, 108 | 109 | replace: function(newText) { 110 | var self = this; 111 | if (this.atOccurrence) 112 | self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to); 113 | } 114 | }; 115 | 116 | CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { 117 | return new SearchCursor(this, query, pos, caseFold); 118 | }); 119 | })(); 120 | -------------------------------------------------------------------------------- /reports/assets/scripts/plato-file.js: -------------------------------------------------------------------------------- 1 | /*global $:false, _:false, Morris:false, CodeMirror:false, __report:false, __history:false */ 2 | /*jshint browser:true*/ 3 | 4 | $(function(){ 5 | "use strict"; 6 | 7 | // bootstrap popover 8 | $('[rel=popover]').popover(); 9 | 10 | _.templateSettings = { 11 | interpolate : /\{\{(.+?)\}\}/g 12 | }; 13 | 14 | function focusFragment() { 15 | $('.plato-mark').removeClass('focus'); 16 | var markId = window.location.hash.substr(1); 17 | if (markId) $('.' + markId).addClass('focus'); 18 | return focusFragment; 19 | } 20 | 21 | window.onhashchange = focusFragment(); 22 | 23 | var srcEl = document.getElementById('file-source'); 24 | 25 | var options = { 26 | lineNumbers : true, 27 | gutters : ['plato-gutter-jshint','plato-gutter-complexity'], 28 | readOnly : 'nocursor' 29 | }; 30 | 31 | var cm = CodeMirror.fromTextArea(srcEl, options); 32 | 33 | var byComplexity = [], bySloc = []; 34 | 35 | var popoverTemplate = _.template($('#complexity-popover-template').text()); 36 | var gutterIcon = $(''); 37 | 38 | var popovers = cm.operation(function(){ 39 | var queuedPopovers = []; 40 | __report.complexity.functions.forEach(function(fn,i){ 41 | byComplexity.push({ 42 | label : fn.name, 43 | value : fn.complexity.cyclomatic 44 | }); 45 | bySloc.push({ 46 | label : fn.name, 47 | value : fn.complexity.sloc.physical, 48 | formatter: function (x) { return x + " lines"; } 49 | }); 50 | 51 | var name = fn.name === '' ? 'function\\s*\\([^)]*\\)' : fn.name; 52 | var line = fn.line - 1; 53 | var className = 'plato-mark-fn-' + i; 54 | var gutter = { 55 | gutterId : 'plato-gutter-complexity', 56 | el : gutterIcon.clone().attr('name',className)[0] 57 | }; 58 | var popover = { 59 | type : 'popover', 60 | title : fn.name === '' ? '<anonymous>' : 'function ' + fn.name + '', 61 | content : popoverTemplate(fn) 62 | }; 63 | queuedPopovers.push(cm.markPopoverText({line : line, ch:0}, name, className, gutter, popover)); 64 | }); 65 | return queuedPopovers; 66 | }); 67 | 68 | popovers.forEach(function(fn){fn();}); 69 | 70 | var scrollToLine = function(i) { 71 | var origScroll = [window.pageXOffset,window.pageYOffset]; 72 | window.location.hash = '#plato-mark-fn-' + i; 73 | window.scrollTo(origScroll[0],origScroll[1]); 74 | var line = __report.complexity.functions[i].line; 75 | var coords = cm.charCoords({line : line, ch : 0}); 76 | $('body,html').animate({scrollTop : coords.top -50},250); 77 | }; 78 | 79 | // yield to the browser 80 | setTimeout(function(){ 81 | drawFunctionCharts([ 82 | { element: 'fn-by-complexity', data: byComplexity }, 83 | { element: 'fn-by-sloc', data: bySloc } 84 | ]); 85 | drawHistoricalCharts(__history); 86 | },0); 87 | 88 | cm.operation(function(){ 89 | addLintMessages(__report); 90 | }); 91 | 92 | 93 | function drawFunctionCharts(charts) { 94 | charts.forEach(function(chart){ 95 | Morris.Donut(chart).on('click',scrollToLine); 96 | }); 97 | } 98 | 99 | function drawHistoricalCharts(history) { 100 | $('.historical.chart').empty(); 101 | var data = _.map(history,function(record){ 102 | var date = new Date(record.date); 103 | return { 104 | date : date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(), 105 | maintainability : parseFloat(record.maintainability).toFixed(2), 106 | sloc : record.sloc 107 | }; 108 | }).slice(-20); 109 | Morris.Line({ 110 | element: 'chart_historical_sloc', 111 | data: data, 112 | xkey: 'date', 113 | ykeys: ['sloc'], 114 | labels: ['Lines of Code'], 115 | parseTime : false 116 | }); 117 | Morris.Line({ 118 | element: 'chart_historical_maint', 119 | data: data, 120 | xkey: 'date', 121 | ykeys: ['maintainability'], 122 | labels: ['Maintainability'], 123 | ymax: 100, 124 | parseTime : false 125 | }); 126 | } 127 | 128 | function addLintMessages(report) { 129 | var lines = {}; 130 | report.jshint.messages.forEach(function (message) { 131 | var text = 'Column: ' + message.column + ' "' + message.message + '"'; 132 | if (_.isArray(message.line)) { 133 | message.line.forEach(function(line){ 134 | if (!lines[line]) lines[line] = ''; 135 | lines[line] += '
      ' + text + '
      '; 136 | }); 137 | } else { 138 | if (!lines[message.line]) lines[message.line] = ''; 139 | lines[message.line] += '
      ' + text + '
      '; 140 | } 141 | }); 142 | var marker = document.createElement('a'); 143 | marker.innerHTML = ''; 144 | Object.keys(lines).forEach(function(line){ 145 | var lineWidget = document.createElement('div'); 146 | lineWidget.innerHTML = lines[line]; 147 | cm.setGutterMarker(line - 1, 'plato-gutter-jshint', marker.cloneNode(true)); 148 | cm.addLineWidget(line - 1, lineWidget); 149 | }); 150 | } 151 | }); 152 | 153 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/pig-hint.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | function forEach(arr, f) { 3 | for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); 4 | } 5 | 6 | function arrayContains(arr, item) { 7 | if (!Array.prototype.indexOf) { 8 | var i = arr.length; 9 | while (i--) { 10 | if (arr[i] === item) { 11 | return true; 12 | } 13 | } 14 | return false; 15 | } 16 | return arr.indexOf(item) != -1; 17 | } 18 | 19 | function scriptHint(editor, _keywords, getToken) { 20 | // Find the token at the cursor 21 | var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; 22 | // If it's not a 'word-style' token, ignore the token. 23 | 24 | if (!/^[\w$_]*$/.test(token.string)) { 25 | token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, 26 | className: token.string == ":" ? "pig-type" : null}; 27 | } 28 | 29 | if (!context) var context = []; 30 | context.push(tprop); 31 | 32 | var completionList = getCompletions(token, context); 33 | completionList = completionList.sort(); 34 | //prevent autocomplete for last word, instead show dropdown with one word 35 | if(completionList.length == 1) { 36 | completionList.push(" "); 37 | } 38 | 39 | return {list: completionList, 40 | from: {line: cur.line, ch: token.start}, 41 | to: {line: cur.line, ch: token.end}}; 42 | } 43 | 44 | CodeMirror.pigHint = function(editor) { 45 | return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); 46 | }; 47 | 48 | var pigKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP " 49 | + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL " 50 | + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE " 51 | + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " 52 | + "NEQ MATCHES TRUE FALSE"; 53 | var pigKeywordsU = pigKeywords.split(" "); 54 | var pigKeywordsL = pigKeywords.toLowerCase().split(" "); 55 | 56 | var pigTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP"; 57 | var pigTypesU = pigTypes.split(" "); 58 | var pigTypesL = pigTypes.toLowerCase().split(" "); 59 | 60 | var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " 61 | + "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS " 62 | + "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG " 63 | + "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN " 64 | + "INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER " 65 | + "ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS " 66 | + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA " 67 | + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE " 68 | + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG " 69 | + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER"; 70 | var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" "); 71 | var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" "); 72 | var pigBuiltinsC = ("BagSize BinStorage Bloom BuildBloom ConstantSize CubeDimensions DoubleAbs " 73 | + "DoubleAvg DoubleBase DoubleMax DoubleMin DoubleRound DoubleSum FloatAbs FloatAvg FloatMax " 74 | + "FloatMin FloatRound FloatSum GenericInvoker IntAbs IntAvg IntMax IntMin IntSum " 75 | + "InvokeForDouble InvokeForFloat InvokeForInt InvokeForLong InvokeForString Invoker " 76 | + "IsEmpty JsonLoader JsonMetadata JsonStorage LongAbs LongAvg LongMax LongMin LongSum MapSize " 77 | + "MonitoredUDF Nondeterministic OutputSchema PigStorage PigStreaming StringConcat StringMax " 78 | + "StringMin StringSize TextLoader TupleSize Utf8StorageConverter").split(" ").join("() ").split(" "); 79 | 80 | function getCompletions(token, context) { 81 | var found = [], start = token.string; 82 | function maybeAdd(str) { 83 | if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); 84 | } 85 | 86 | function gatherCompletions(obj) { 87 | if(obj == ":") { 88 | forEach(pigTypesL, maybeAdd); 89 | } 90 | else { 91 | forEach(pigBuiltinsU, maybeAdd); 92 | forEach(pigBuiltinsL, maybeAdd); 93 | forEach(pigBuiltinsC, maybeAdd); 94 | forEach(pigTypesU, maybeAdd); 95 | forEach(pigTypesL, maybeAdd); 96 | forEach(pigKeywordsU, maybeAdd); 97 | forEach(pigKeywordsL, maybeAdd); 98 | } 99 | } 100 | 101 | if (context) { 102 | // If this is a property, see if it belongs to some object we can 103 | // find in the current environment. 104 | var obj = context.pop(), base; 105 | 106 | if (obj.type == "variable") 107 | base = obj.string; 108 | else if(obj.type == "variable-3") 109 | base = ":" + obj.string; 110 | 111 | while (base != null && context.length) 112 | base = base[context.pop().string]; 113 | if (base != null) gatherCompletions(base); 114 | } 115 | return found; 116 | } 117 | })(); 118 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/search.js: -------------------------------------------------------------------------------- 1 | // Define search commands. Depends on dialog.js or another 2 | // implementation of the openDialog method. 3 | 4 | // Replace works a little oddly -- it will do the replace on the next 5 | // Ctrl-G (or whatever is bound to findNext) press. You prevent a 6 | // replace by making sure the match is no longer selected when hitting 7 | // Ctrl-G. 8 | 9 | (function() { 10 | function SearchState() { 11 | this.posFrom = this.posTo = this.query = null; 12 | this.marked = []; 13 | } 14 | function getSearchState(cm) { 15 | return cm._searchState || (cm._searchState = new SearchState()); 16 | } 17 | function getSearchCursor(cm, query, pos) { 18 | // Heuristic: if the query string is all lowercase, do a case insensitive search. 19 | return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase()); 20 | } 21 | function dialog(cm, text, shortText, f) { 22 | if (cm.openDialog) cm.openDialog(text, f); 23 | else f(prompt(shortText, "")); 24 | } 25 | function confirmDialog(cm, text, shortText, fs) { 26 | if (cm.openConfirm) cm.openConfirm(text, fs); 27 | else if (confirm(shortText)) fs[0](); 28 | } 29 | function parseQuery(query) { 30 | var isRE = query.match(/^\/(.*)\/([a-z]*)$/); 31 | return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query; 32 | } 33 | var queryDialog = 34 | 'Search: (Use /re/ syntax for regexp search)'; 35 | function doSearch(cm, rev) { 36 | var state = getSearchState(cm); 37 | if (state.query) return findNext(cm, rev); 38 | dialog(cm, queryDialog, "Search for:", function(query) { 39 | cm.operation(function() { 40 | if (!query || state.query) return; 41 | state.query = parseQuery(query); 42 | if (cm.lineCount() < 2000) { // This is too expensive on big documents. 43 | for (var cursor = getSearchCursor(cm, state.query); cursor.findNext();) 44 | state.marked.push(cm.markText(cursor.from(), cursor.to(), 45 | {className: "CodeMirror-searching"})); 46 | } 47 | state.posFrom = state.posTo = cm.getCursor(); 48 | findNext(cm, rev); 49 | }); 50 | }); 51 | } 52 | function findNext(cm, rev) {cm.operation(function() { 53 | var state = getSearchState(cm); 54 | var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo); 55 | if (!cursor.find(rev)) { 56 | cursor = getSearchCursor(cm, state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0}); 57 | if (!cursor.find(rev)) return; 58 | } 59 | cm.setSelection(cursor.from(), cursor.to()); 60 | state.posFrom = cursor.from(); state.posTo = cursor.to(); 61 | });} 62 | function clearSearch(cm) {cm.operation(function() { 63 | var state = getSearchState(cm); 64 | if (!state.query) return; 65 | state.query = null; 66 | for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); 67 | state.marked.length = 0; 68 | });} 69 | 70 | var replaceQueryDialog = 71 | 'Replace: (Use /re/ syntax for regexp search)'; 72 | var replacementQueryDialog = 'With: '; 73 | var doReplaceConfirm = "Replace? "; 74 | function replace(cm, all) { 75 | dialog(cm, replaceQueryDialog, "Replace:", function(query) { 76 | if (!query) return; 77 | query = parseQuery(query); 78 | dialog(cm, replacementQueryDialog, "Replace with:", function(text) { 79 | if (all) { 80 | cm.operation(function() { 81 | for (var cursor = getSearchCursor(cm, query); cursor.findNext();) { 82 | if (typeof query != "string") { 83 | var match = cm.getRange(cursor.from(), cursor.to()).match(query); 84 | cursor.replace(text.replace(/\$(\d)/, function(_, i) {return match[i];})); 85 | } else cursor.replace(text); 86 | } 87 | }); 88 | } else { 89 | clearSearch(cm); 90 | var cursor = getSearchCursor(cm, query, cm.getCursor()); 91 | function advance() { 92 | var start = cursor.from(), match; 93 | if (!(match = cursor.findNext())) { 94 | cursor = getSearchCursor(cm, query); 95 | if (!(match = cursor.findNext()) || 96 | (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return; 97 | } 98 | cm.setSelection(cursor.from(), cursor.to()); 99 | confirmDialog(cm, doReplaceConfirm, "Replace?", 100 | [function() {doReplace(match);}, advance]); 101 | } 102 | function doReplace(match) { 103 | cursor.replace(typeof query == "string" ? text : 104 | text.replace(/\$(\d)/, function(_, i) {return match[i];})); 105 | advance(); 106 | } 107 | advance(); 108 | } 109 | }); 110 | }); 111 | } 112 | 113 | CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);}; 114 | CodeMirror.commands.findNext = doSearch; 115 | CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);}; 116 | CodeMirror.commands.clearSearch = clearSearch; 117 | CodeMirror.commands.replace = replace; 118 | CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);}; 119 | })(); 120 | -------------------------------------------------------------------------------- /reports/assets/scripts/plato-overview.js: -------------------------------------------------------------------------------- 1 | /*global $:false, _:false, Morris:false, __report:false, __history:false, __options: false */ 2 | /*jshint browser:true*/ 3 | 4 | $(function(){ 5 | "use strict"; 6 | 7 | // bootstrap popover 8 | $('[rel=popover]').popover(); 9 | 10 | // @todo put client side templates into a JST 11 | var fileGraphTemplate = _.template( 12 | '
      ' + 13 | '' + 14 | '' + 15 | '<%= value %>' + 16 | '
      ' 17 | ); 18 | 19 | var horizontalBar = function(orig, width, label, thresholds){ 20 | var threshold = 0; 21 | for (var i = thresholds.length - 1; i > -1; i--) { 22 | if (orig > thresholds[i]) { 23 | threshold = i + 1; 24 | break; 25 | } 26 | } 27 | return fileGraphTemplate({ 28 | width : width, 29 | label : label, 30 | threshold : threshold, 31 | value : orig 32 | }); 33 | }; 34 | 35 | function drawFileCharts() { 36 | // @todo make a jQuery plugin to accomodate the horizontalBar function 37 | $('.js-file-chart').each(function(){ 38 | var el = $(this), 39 | width = el.width() - 130; // @todo establish max width of graph in plugin 40 | 41 | el.empty(); 42 | 43 | var value = el.data('complexity'); 44 | el.append(horizontalBar(value, Math.min(value * 2, width),'complexity', [5,10])); 45 | 46 | value = el.data('sloc'); 47 | el.append(horizontalBar(value, Math.min(value, width), 'sloc', [400,600])); 48 | 49 | value = el.data('bugs'); 50 | el.append(horizontalBar(value, Math.min(value * 5, width), 'est errors', [1,5])); 51 | 52 | value = el.data('lint'); 53 | el.append(horizontalBar(value, Math.min(value * 5, width), 'lint errors', [1,10])); 54 | }); 55 | } 56 | 57 | function drawOverviewCharts(reports) { 58 | 59 | var maintainability = { 60 | element: 'chart_maintainability', 61 | data: [], 62 | xkey: 'label', 63 | ykeys: ['value'], 64 | ymax : 100, 65 | ymin : 0, 66 | labels: ['Maintainability'], 67 | barColors : ['#ff9b40'] 68 | }; 69 | var sloc = { 70 | element: 'chart_sloc', 71 | data: [], 72 | xkey: 'label', 73 | ykeys: ['value'], 74 | ymax : 400, 75 | labels: ['Lines'], 76 | barColors : ['#1f6b75'] 77 | }; 78 | var bugs = { 79 | element: 'chart_bugs', 80 | data: [], 81 | xkey: 'label', 82 | ykeys: ['value'], 83 | labels: ['Errors'], 84 | ymax: 20, 85 | barColors : ['#ff9b40'] 86 | }; 87 | var lint = { 88 | element: 'chart_lint', 89 | data: [], 90 | xkey: 'label', 91 | ykeys: ['value'], 92 | labels: ['Errors'], 93 | ymax: 20, 94 | barColors : ['#1f6b75'] 95 | }; 96 | 97 | reports.forEach(function(report){ 98 | 99 | // @todo shouldn't need this, 'auto [num]' doesn't seem to work : https://github.com/oesmith/morris.js/issues/201 100 | sloc.ymax = Math.max(sloc.ymax, report.complexity.aggregate.complexity.sloc.physical); 101 | bugs.ymax = Math.max(bugs.ymax, report.complexity.aggregate.complexity.halstead.bugs.toFixed(2)); 102 | 103 | 104 | sloc.data.push({ 105 | value : report.complexity.aggregate.complexity.sloc.physical, 106 | label : report.info.fileShort 107 | }); 108 | bugs.data.push({ 109 | value : report.complexity.aggregate.complexity.halstead.bugs.toFixed(2), 110 | label : report.info.fileShort 111 | }); 112 | maintainability.data.push({ 113 | value : report.complexity.maintainability ? report.complexity.maintainability.toFixed(2) : 0, 114 | label : report.info.fileShort 115 | }); 116 | lint.data.push({ 117 | value : report.jshint && report.jshint.messages, 118 | label : report.info.fileShort 119 | }); 120 | }); 121 | 122 | function onGraphClick(i){ 123 | document.location = __report.reports[i].info.link; 124 | } 125 | 126 | var charts = [ 127 | Morris.Bar(bugs), 128 | Morris.Bar(sloc), 129 | Morris.Bar(maintainability) 130 | ]; 131 | 132 | if (__options.flags.jshint) charts.push(Morris.Bar(lint)); 133 | 134 | charts.forEach(function(chart){ 135 | chart.on('click', onGraphClick); 136 | }); 137 | return charts; 138 | } 139 | 140 | function drawHistoricalChart(history) { 141 | var data = _.map(history,function(record){ 142 | var date = new Date(record.date); 143 | return { 144 | date : date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(), 145 | average_maintainability : parseFloat(record.average.maintainability), 146 | average_sloc : record.average.sloc 147 | }; 148 | }).slice(-20); 149 | Morris.Line({ 150 | element: 'chart_historical_sloc', 151 | data: data, 152 | xkey: 'date', 153 | ykeys: ['average_sloc'], 154 | labels: ['Average Lines'], 155 | parseTime : false 156 | }); 157 | Morris.Line({ 158 | element: 'chart_historical_maint', 159 | data: data, 160 | xkey: 'date', 161 | ykeys: ['average_maintainability'], 162 | labels: ['Maintainability'], 163 | ymax: 100, 164 | parseTime : false 165 | }); 166 | } 167 | 168 | function drawCharts() { 169 | $('.js-chart').empty(); 170 | drawHistoricalChart(__history); 171 | drawOverviewCharts(__report.reports); 172 | drawFileCharts(__report.reports); 173 | } 174 | 175 | drawCharts(); 176 | 177 | $(window).on('resize', _.debounce(drawCharts,200)); 178 | }); 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/javascript-hint.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | function forEach(arr, f) { 3 | for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); 4 | } 5 | 6 | function arrayContains(arr, item) { 7 | if (!Array.prototype.indexOf) { 8 | var i = arr.length; 9 | while (i--) { 10 | if (arr[i] === item) { 11 | return true; 12 | } 13 | } 14 | return false; 15 | } 16 | return arr.indexOf(item) != -1; 17 | } 18 | 19 | function scriptHint(editor, keywords, getToken, options) { 20 | // Find the token at the cursor 21 | var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; 22 | // If it's not a 'word-style' token, ignore the token. 23 | if (!/^[\w$_]*$/.test(token.string)) { 24 | token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, 25 | type: token.string == "." ? "property" : null}; 26 | } 27 | // If it is a property, find out what it is a property of. 28 | while (tprop.type == "property") { 29 | tprop = getToken(editor, {line: cur.line, ch: tprop.start}); 30 | if (tprop.string != ".") return; 31 | tprop = getToken(editor, {line: cur.line, ch: tprop.start}); 32 | if (tprop.string == ')') { 33 | var level = 1; 34 | do { 35 | tprop = getToken(editor, {line: cur.line, ch: tprop.start}); 36 | switch (tprop.string) { 37 | case ')': level++; break; 38 | case '(': level--; break; 39 | default: break; 40 | } 41 | } while (level > 0); 42 | tprop = getToken(editor, {line: cur.line, ch: tprop.start}); 43 | if (tprop.type == 'variable') 44 | tprop.type = 'function'; 45 | else return; // no clue 46 | } 47 | if (!context) var context = []; 48 | context.push(tprop); 49 | } 50 | return {list: getCompletions(token, context, keywords, options), 51 | from: {line: cur.line, ch: token.start}, 52 | to: {line: cur.line, ch: token.end}}; 53 | } 54 | 55 | CodeMirror.javascriptHint = function(editor, options) { 56 | return scriptHint(editor, javascriptKeywords, 57 | function (e, cur) {return e.getTokenAt(cur);}, 58 | options); 59 | }; 60 | 61 | function getCoffeeScriptToken(editor, cur) { 62 | // This getToken, it is for coffeescript, imitates the behavior of 63 | // getTokenAt method in javascript.js, that is, returning "property" 64 | // type and treat "." as indepenent token. 65 | var token = editor.getTokenAt(cur); 66 | if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') { 67 | token.end = token.start; 68 | token.string = '.'; 69 | token.type = "property"; 70 | } 71 | else if (/^\.[\w$_]*$/.test(token.string)) { 72 | token.type = "property"; 73 | token.start++; 74 | token.string = token.string.replace(/\./, ''); 75 | } 76 | return token; 77 | } 78 | 79 | CodeMirror.coffeescriptHint = function(editor, options) { 80 | return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options); 81 | }; 82 | 83 | var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + 84 | "toUpperCase toLowerCase split concat match replace search").split(" "); 85 | var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " + 86 | "lastIndexOf every some filter forEach map reduce reduceRight ").split(" "); 87 | var funcProps = "prototype apply call bind".split(" "); 88 | var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " + 89 | "if in instanceof new null return switch throw true try typeof var void while with").split(" "); 90 | var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " + 91 | "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" "); 92 | 93 | function getCompletions(token, context, keywords, options) { 94 | var found = [], start = token.string; 95 | function maybeAdd(str) { 96 | if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); 97 | } 98 | function gatherCompletions(obj) { 99 | if (typeof obj == "string") forEach(stringProps, maybeAdd); 100 | else if (obj instanceof Array) forEach(arrayProps, maybeAdd); 101 | else if (obj instanceof Function) forEach(funcProps, maybeAdd); 102 | for (var name in obj) maybeAdd(name); 103 | } 104 | 105 | if (context) { 106 | // If this is a property, see if it belongs to some object we can 107 | // find in the current environment. 108 | var obj = context.pop(), base; 109 | if (obj.type == "variable") { 110 | if (options && options.additionalContext) 111 | base = options.additionalContext[obj.string]; 112 | base = base || window[obj.string]; 113 | } else if (obj.type == "string") { 114 | base = ""; 115 | } else if (obj.type == "atom") { 116 | base = 1; 117 | } else if (obj.type == "function") { 118 | if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') && 119 | (typeof window.jQuery == 'function')) 120 | base = window.jQuery(); 121 | else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function')) 122 | base = window._(); 123 | } 124 | while (base != null && context.length) 125 | base = base[context.pop().string]; 126 | if (base != null) gatherCompletions(base); 127 | } 128 | else { 129 | // If not, just look in the window object and any local scope 130 | // (reading into JS mode internals to get at the local variables) 131 | for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name); 132 | gatherCompletions(window); 133 | forEach(keywords, maybeAdd); 134 | } 135 | return found; 136 | } 137 | })(); 138 | -------------------------------------------------------------------------------- /reports/files/amd_js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 37 | 38 |
      39 |
      40 |

      amd.js

      41 |
      42 |
      43 | 44 |
      45 |
      46 |
      47 |

      Maintainability

      48 |

      72.11

      49 |
      50 |
      51 |

      Lines of code

      52 |

      20

      53 |
      54 |
      55 |
      56 |
      57 |

      58 |
      59 |
      60 |

      61 |
      62 |
      63 |
      64 |
      65 |

      Difficulty

      66 |

      10.11

      67 |
      68 |
      69 |

      Estimated Errors

      70 |

      0.09

      71 |
      72 |
      73 |
      74 | 75 |
      76 |
      77 |

      Function weight

      78 |
      79 |
      80 |
      81 |

      By Complexity

      82 |
      83 |
      84 |
      85 |

      By SLOC

      86 |
      87 |
      88 |
      89 |
      90 | 91 |
      92 |
      93 | 113 |
      114 |
      115 | 116 |
      117 |
      118 |

      .

      119 |
      120 |
      121 | 122 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /lib/backbone.picky.min.js: -------------------------------------------------------------------------------- 1 | // Backbone.Picky, v0.2.0 2 | // Copyright (c)2014 Derick Bailey, Muted Solutions, LLC. 3 | // Distributed under MIT license 4 | // http://github.com/derickbailey/backbone.picky 5 | 6 | 7 | Backbone.Picky=function(a,b){function c(a,b){h(a,b),a.selected&&b.select(a,{_silentReselect:!0,_externalEvent:"add"})}function d(a,c,d){e(a,c,b.extend({},d,{_externalEvent:"remove"}))}function e(a,c,d){a._pickyCollections&&(a._pickyCollections=b.without(a._pickyCollections,c._pickyCid)),a.selected&&(a._pickyCollections&&0===a._pickyCollections.length?c.deselect(a,d):c.deselect(a,b.extend({},d,{_skipModelCall:!0})))}function f(a,c){var d,f,g=b.find(c.previousModels,function(a){return a.selected});g&&e(g,a,{_silentLocally:!0}),d=a.filter(function(a){return a.selected}),f=b.initial(d),f.length&&b.each(f,function(a){a.deselect()}),d.length&&a.select(b.last(d),{silent:!0})}function g(a,c){var d,f=b.filter(c.previousModels,function(a){return a.selected});f&&b.each(f,function(b){e(b,a,{_silentLocally:!0})}),d=a.filter(function(a){return a.selected}),d.length&&b.each(d,function(b){a.select(b,{silent:!0})})}function h(a,b){a._pickyCollections||(a._pickyCollections=[]),a._pickyCollections.push(b._pickyCid)}function i(a){a.each(function(b){e(b,a,{_silentLocally:!0})})}function j(a){return b.omit(a,"_silentLocally","_externalEvent")}function k(a){return b.omit(a,"_silentLocally","_silentReselect","_skipModelCall","_processedBy")}function l(a){function c(a,b){d[b]=a}var d=[];return b.each(a,c),d}function m(a){function c(a,b,c){return c.toUpperCase()}function d(a){return"ed"===a.slice(-2)?a=a.slice(0,-2):(":one"===a.slice(-4)||":any"===a.slice(-4))&&(a=a.slice(0,-4)),a}var e=a.trigger,f=/(^|:)(\w)/gi;return function(a){var g=d(a),h="on"+g.replace(f,c),i=this[h];return b.isFunction(i)&&i.apply(this,b.tail(arguments)),e.apply(this,arguments),this}}var n={};n.SingleSelect=function(a,e){this._pickyCid=b.uniqueId("singleSelect"),this.collection=a,this.trigger=m(a),arguments.length>1&&(b.each(e||[],function(a){h(a,this),a.selected&&(this.selected&&this.selected.deselect(),this.selected=a)},this),this.collection.listenTo(this.collection,"_selected",this.select),this.collection.listenTo(this.collection,"_deselected",this.deselect),this.collection.listenTo(this.collection,"reset",f),this.collection.listenTo(this.collection,"add",c),this.collection.listenTo(this.collection,"remove",d),this._modelSharingEnabled=!0)},b.extend(n.SingleSelect.prototype,{select:function(a,c){var d=a&&this.selected===a?a:void 0;c||(c={}),c._processedBy||(c._processedBy=[]),c._processedBy[this._pickyCid]||(d||(this.deselect(void 0,b.omit(c,"_silentLocally")),this.selected=a),c._processedBy[this._pickyCid]=this,c._processedBy[this.selected.cid]||this.selected.select(j(c)),c.silent||c._silentLocally||(d?c._silentReselect||this.trigger("reselect:one",a,this,k(c)):this.trigger("select:one",a,this,k(c))))},deselect:function(a,b){b||(b={}),this.selected&&(a=a||this.selected,this.selected===a&&(delete this.selected,b._skipModelCall||a.deselect(j(b)),b.silent||b._silentLocally||this.trigger("deselect:one",a,this,k(b))))},close:function(){i(this),this.stopListening()}}),n.MultiSelect=function(a,e){this._pickyCid=b.uniqueId("multiSelect"),this.collection=a,this.selected={},this.trigger=m(a),arguments.length>1&&(b.each(e||[],function(a){h(a,this),a.selected&&(this.selected[a.cid]=a)},this),this.collection.listenTo(this.collection,"_selected",this.select),this.collection.listenTo(this.collection,"_deselected",this.deselect),this.collection.listenTo(this.collection,"reset",g),this.collection.listenTo(this.collection,"add",c),this.collection.listenTo(this.collection,"remove",d),this._modelSharingEnabled=!0)},b.extend(n.MultiSelect.prototype,{select:function(a,c){var d=l(this.selected),e=this.selected[a.cid]?[a]:[];c||(c={}),c._processedBy||(c._processedBy=[]),e.length&&c._processedBy[this._pickyCid]||(e.length||(this.selected[a.cid]=a,this.selectedLength=b.size(this.selected)),c._processedBy[this._pickyCid]=this,c._processedBy[a.cid]||a.select(j(c)),o(this,d,c,e))},deselect:function(a,c){var d=l(this.selected);c||(c={}),this.selected[a.cid]&&(delete this.selected[a.cid],this.selectedLength=b.size(this.selected),c._skipModelCall||a.deselect(j(c)),o(this,d,c))},selectAll:function(a){var c=l(this.selected),d=[];a||(a={}),a._processedBy||(a._processedBy=[]),this.selectedLength=0,this.each(function(c){this.selectedLength++,this.selected[c.cid]&&d.push(c),this.select(c,b.extend({},a,{_silentLocally:!0}))},this),a._processedBy[this._pickyCid]=this,o(this,c,a,d)},deselectAll:function(a){var c;0!==this.selectedLength&&(c=l(this.selected),this.each(function(c){c.selected&&this.selectedLength--,this.deselect(c,b.extend({},a,{_silentLocally:!0}))},this),this.selectedLength=0,o(this,c,a))},selectNone:function(a){this.deselectAll(a)},toggleSelectAll:function(a){this.selectedLength===this.length?this.deselectAll(a):this.selectAll(a)},close:function(){i(this),this.stopListening()}}),n.Selectable=function(a){this.model=a,this.trigger=m(a)},b.extend(n.Selectable.prototype,{select:function(a){var b=this.selected;a||(a={}),a._processedBy||(a._processedBy=[]),a._processedBy[this.cid]||(this.selected=!0,a._processedBy[this.cid]=this,this._pickyCollections?this.trigger("_selected",this,j(a)):this.collection&&(a._processedBy[this.collection._pickyCid]||this.collection.select(this,j(a))),a.silent||a._silentLocally||(b?a._silentReselect||this.trigger("reselected",this,k(a)):this.trigger("selected",this,k(a))))},deselect:function(a){a||(a={}),this.selected&&(this.selected=!1,this._pickyCollections?this.trigger("_deselected",this,j(a)):this.collection&&this.collection.deselect(this,j(a)),a.silent||a._silentLocally||this.trigger("deselected",this,k(a)))},toggleSelected:function(a){this.selected?this.deselect(a):this.select(a)}}),n.Selectable.applyTo=function(c){b.extend(c,new a.Picky.Selectable(c))},n.SingleSelect.applyTo=function(c,d){var e;e=arguments.length<2?new a.Picky.SingleSelect(c):new a.Picky.SingleSelect(c,d),b.extend(c,e)},n.MultiSelect.applyTo=function(c,d){var e;e=arguments.length<2?new a.Picky.MultiSelect(c):new a.Picky.MultiSelect(c,d),b.extend(c,e)};var o=function(a,c,d,e){function f(a,c,d){function e(a){return c.get(a)||d[a]}return b.map(a,e)}if(d||(d={}),!d.silent&&!d._silentLocally){var g,h=a.selectedLength,i=a.length,j=b.keys(c),l=b.keys(a.selected),m=b.difference(l,j),n=b.difference(j,l),o=h===j.length&&0===m.length&&0===n.length;if(e&&e.length&&!d._silentReselect&&a.trigger("reselect:any",e,a,k(d)),!o)return g={selected:f(m,a,c),deselected:f(n,a,c)},h===i?void a.trigger("select:all",g,a,k(d)):0===h?void a.trigger("select:none",g,a,k(d)):h>0&&i>h?void a.trigger("select:some",g,a,k(d)):void 0}};return n}(Backbone,_); 8 | //# sourceMappingURL=backbone.picky.map -------------------------------------------------------------------------------- /lib/amd/backbone.picky.min.js: -------------------------------------------------------------------------------- 1 | // Backbone.Picky, v0.2.0 2 | // Copyright (c)2014 Derick Bailey, Muted Solutions, LLC. 3 | // Distributed under MIT license 4 | // http://github.com/derickbailey/backbone.picky 5 | 6 | !function(a,b){if("object"==typeof exports){var c=require("underscore"),d=require("backbone");module.exports=b(c,d)}else"function"==typeof define&&define.amd&&define(["underscore","backbone"],b)}(this,function(a,b){"option strict";return b.Picky=function(a,b){function c(a,b){h(a,b),a.selected&&b.select(a,{_silentReselect:!0,_externalEvent:"add"})}function d(a,c,d){e(a,c,b.extend({},d,{_externalEvent:"remove"}))}function e(a,c,d){a._pickyCollections&&(a._pickyCollections=b.without(a._pickyCollections,c._pickyCid)),a.selected&&(a._pickyCollections&&0===a._pickyCollections.length?c.deselect(a,d):c.deselect(a,b.extend({},d,{_skipModelCall:!0})))}function f(a,c){var d,f,g=b.find(c.previousModels,function(a){return a.selected});g&&e(g,a,{_silentLocally:!0}),d=a.filter(function(a){return a.selected}),f=b.initial(d),f.length&&b.each(f,function(a){a.deselect()}),d.length&&a.select(b.last(d),{silent:!0})}function g(a,c){var d,f=b.filter(c.previousModels,function(a){return a.selected});f&&b.each(f,function(b){e(b,a,{_silentLocally:!0})}),d=a.filter(function(a){return a.selected}),d.length&&b.each(d,function(b){a.select(b,{silent:!0})})}function h(a,b){a._pickyCollections||(a._pickyCollections=[]),a._pickyCollections.push(b._pickyCid)}function i(a){a.each(function(b){e(b,a,{_silentLocally:!0})})}function j(a){return b.omit(a,"_silentLocally","_externalEvent")}function k(a){return b.omit(a,"_silentLocally","_silentReselect","_skipModelCall","_processedBy")}function l(a){function c(a,b){d[b]=a}var d=[];return b.each(a,c),d}function m(a){function c(a,b,c){return c.toUpperCase()}function d(a){return"ed"===a.slice(-2)?a=a.slice(0,-2):(":one"===a.slice(-4)||":any"===a.slice(-4))&&(a=a.slice(0,-4)),a}var e=a.trigger,f=/(^|:)(\w)/gi;return function(a){var g=d(a),h="on"+g.replace(f,c),i=this[h];return b.isFunction(i)&&i.apply(this,b.tail(arguments)),e.apply(this,arguments),this}}var n={};n.SingleSelect=function(a,e){this._pickyCid=b.uniqueId("singleSelect"),this.collection=a,this.trigger=m(a),arguments.length>1&&(b.each(e||[],function(a){h(a,this),a.selected&&(this.selected&&this.selected.deselect(),this.selected=a)},this),this.collection.listenTo(this.collection,"_selected",this.select),this.collection.listenTo(this.collection,"_deselected",this.deselect),this.collection.listenTo(this.collection,"reset",f),this.collection.listenTo(this.collection,"add",c),this.collection.listenTo(this.collection,"remove",d),this._modelSharingEnabled=!0)},b.extend(n.SingleSelect.prototype,{select:function(a,c){var d=a&&this.selected===a?a:void 0;c||(c={}),c._processedBy||(c._processedBy=[]),c._processedBy[this._pickyCid]||(d||(this.deselect(void 0,b.omit(c,"_silentLocally")),this.selected=a),c._processedBy[this._pickyCid]=this,c._processedBy[this.selected.cid]||this.selected.select(j(c)),c.silent||c._silentLocally||(d?c._silentReselect||this.trigger("reselect:one",a,this,k(c)):this.trigger("select:one",a,this,k(c))))},deselect:function(a,b){b||(b={}),this.selected&&(a=a||this.selected,this.selected===a&&(delete this.selected,b._skipModelCall||a.deselect(j(b)),b.silent||b._silentLocally||this.trigger("deselect:one",a,this,k(b))))},close:function(){i(this),this.stopListening()}}),n.MultiSelect=function(a,e){this._pickyCid=b.uniqueId("multiSelect"),this.collection=a,this.selected={},this.trigger=m(a),arguments.length>1&&(b.each(e||[],function(a){h(a,this),a.selected&&(this.selected[a.cid]=a)},this),this.collection.listenTo(this.collection,"_selected",this.select),this.collection.listenTo(this.collection,"_deselected",this.deselect),this.collection.listenTo(this.collection,"reset",g),this.collection.listenTo(this.collection,"add",c),this.collection.listenTo(this.collection,"remove",d),this._modelSharingEnabled=!0)},b.extend(n.MultiSelect.prototype,{select:function(a,c){var d=l(this.selected),e=this.selected[a.cid]?[a]:[];c||(c={}),c._processedBy||(c._processedBy=[]),e.length&&c._processedBy[this._pickyCid]||(e.length||(this.selected[a.cid]=a,this.selectedLength=b.size(this.selected)),c._processedBy[this._pickyCid]=this,c._processedBy[a.cid]||a.select(j(c)),o(this,d,c,e))},deselect:function(a,c){var d=l(this.selected);c||(c={}),this.selected[a.cid]&&(delete this.selected[a.cid],this.selectedLength=b.size(this.selected),c._skipModelCall||a.deselect(j(c)),o(this,d,c))},selectAll:function(a){var c=l(this.selected),d=[];a||(a={}),a._processedBy||(a._processedBy=[]),this.selectedLength=0,this.each(function(c){this.selectedLength++,this.selected[c.cid]&&d.push(c),this.select(c,b.extend({},a,{_silentLocally:!0}))},this),a._processedBy[this._pickyCid]=this,o(this,c,a,d)},deselectAll:function(a){var c;0!==this.selectedLength&&(c=l(this.selected),this.each(function(c){c.selected&&this.selectedLength--,this.deselect(c,b.extend({},a,{_silentLocally:!0}))},this),this.selectedLength=0,o(this,c,a))},selectNone:function(a){this.deselectAll(a)},toggleSelectAll:function(a){this.selectedLength===this.length?this.deselectAll(a):this.selectAll(a)},close:function(){i(this),this.stopListening()}}),n.Selectable=function(a){this.model=a,this.trigger=m(a)},b.extend(n.Selectable.prototype,{select:function(a){var b=this.selected;a||(a={}),a._processedBy||(a._processedBy=[]),a._processedBy[this.cid]||(this.selected=!0,a._processedBy[this.cid]=this,this._pickyCollections?this.trigger("_selected",this,j(a)):this.collection&&(a._processedBy[this.collection._pickyCid]||this.collection.select(this,j(a))),a.silent||a._silentLocally||(b?a._silentReselect||this.trigger("reselected",this,k(a)):this.trigger("selected",this,k(a))))},deselect:function(a){a||(a={}),this.selected&&(this.selected=!1,this._pickyCollections?this.trigger("_deselected",this,j(a)):this.collection&&this.collection.deselect(this,j(a)),a.silent||a._silentLocally||this.trigger("deselected",this,k(a)))},toggleSelected:function(a){this.selected?this.deselect(a):this.select(a)}}),n.Selectable.applyTo=function(c){b.extend(c,new a.Picky.Selectable(c))},n.SingleSelect.applyTo=function(c,d){var e;e=arguments.length<2?new a.Picky.SingleSelect(c):new a.Picky.SingleSelect(c,d),b.extend(c,e)},n.MultiSelect.applyTo=function(c,d){var e;e=arguments.length<2?new a.Picky.MultiSelect(c):new a.Picky.MultiSelect(c,d),b.extend(c,e)};var o=function(a,c,d,e){function f(a,c,d){function e(a){return c.get(a)||d[a]}return b.map(a,e)}if(d||(d={}),!d.silent&&!d._silentLocally){var g,h=a.selectedLength,i=a.length,j=b.keys(c),l=b.keys(a.selected),m=b.difference(l,j),n=b.difference(j,l),o=h===j.length&&0===m.length&&0===n.length;if(e&&e.length&&!d._silentReselect&&a.trigger("reselect:any",e,a,k(d)),!o)return g={selected:f(m,a,c),deselected:f(n,a,c)},h===i?void a.trigger("select:all",g,a,k(d)):0===h?void a.trigger("select:none",g,a,k(d)):h>0&&i>h?void a.trigger("select:some",g,a,k(d)):void 0}};return n}(b,a),b.Picky}); -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/codemirror/util/foldcode.js: -------------------------------------------------------------------------------- 1 | // the tagRangeFinder function is 2 | // Copyright (C) 2011 by Daniel Glazman 3 | // released under the MIT license (../../LICENSE) like the rest of CodeMirror 4 | CodeMirror.tagRangeFinder = function(cm, start) { 5 | var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; 6 | var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; 7 | var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*"); 8 | 9 | var lineText = cm.getLine(start.line); 10 | var found = false; 11 | var tag = null; 12 | var pos = start.ch; 13 | while (!found) { 14 | pos = lineText.indexOf("<", pos); 15 | if (-1 == pos) // no tag on line 16 | return; 17 | if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag 18 | pos++; 19 | continue; 20 | } 21 | // ok we seem to have a start tag 22 | if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name... 23 | pos++; 24 | continue; 25 | } 26 | var gtPos = lineText.indexOf(">", pos + 1); 27 | if (-1 == gtPos) { // end of start tag not in line 28 | var l = start.line + 1; 29 | var foundGt = false; 30 | var lastLine = cm.lineCount(); 31 | while (l < lastLine && !foundGt) { 32 | var lt = cm.getLine(l); 33 | gtPos = lt.indexOf(">"); 34 | if (-1 != gtPos) { // found a > 35 | foundGt = true; 36 | var slash = lt.lastIndexOf("/", gtPos); 37 | if (-1 != slash && slash < gtPos) { 38 | var str = lineText.substr(slash, gtPos - slash + 1); 39 | if (!str.match( /\/\s*\>/ )) // yep, that's the end of empty tag 40 | return; 41 | } 42 | } 43 | l++; 44 | } 45 | found = true; 46 | } 47 | else { 48 | var slashPos = lineText.lastIndexOf("/", gtPos); 49 | if (-1 == slashPos) { // cannot be empty tag 50 | found = true; 51 | // don't continue 52 | } 53 | else { // empty tag? 54 | // check if really empty tag 55 | var str = lineText.substr(slashPos, gtPos - slashPos + 1); 56 | if (!str.match( /\/\s*\>/ )) { // finally not empty 57 | found = true; 58 | // don't continue 59 | } 60 | } 61 | } 62 | if (found) { 63 | var subLine = lineText.substr(pos + 1); 64 | tag = subLine.match(xmlNAMERegExp); 65 | if (tag) { 66 | // we have an element name, wooohooo ! 67 | tag = tag[0]; 68 | // do we have the close tag on same line ??? 69 | if (-1 != lineText.indexOf("", pos)) // yep 70 | { 71 | found = false; 72 | } 73 | // we don't, so we have a candidate... 74 | } 75 | else 76 | found = false; 77 | } 78 | if (!found) 79 | pos++; 80 | } 81 | 82 | if (found) { 83 | var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)"; 84 | var startTagRegExp = new RegExp(startTag); 85 | var endTag = ""; 86 | var depth = 1; 87 | var l = start.line + 1; 88 | var lastLine = cm.lineCount(); 89 | while (l < lastLine) { 90 | lineText = cm.getLine(l); 91 | var match = lineText.match(startTagRegExp); 92 | if (match) { 93 | for (var i = 0; i < match.length; i++) { 94 | if (match[i] == endTag) 95 | depth--; 96 | else 97 | depth++; 98 | if (!depth) return {from: {line: start.line, ch: gtPos + 1}, 99 | to: {line: l, ch: match.index}}; 100 | } 101 | } 102 | l++; 103 | } 104 | return; 105 | } 106 | }; 107 | 108 | CodeMirror.braceRangeFinder = function(cm, start) { 109 | var line = start.line, lineText = cm.getLine(line); 110 | var at = lineText.length, startChar, tokenType; 111 | for (;;) { 112 | var found = lineText.lastIndexOf("{", at); 113 | if (found < start.ch) break; 114 | tokenType = cm.getTokenAt({line: line, ch: found}).type; 115 | if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; } 116 | at = found - 1; 117 | } 118 | if (startChar == null || lineText.lastIndexOf("}") > startChar) return; 119 | var count = 1, lastLine = cm.lineCount(), end, endCh; 120 | outer: for (var i = line + 1; i < lastLine; ++i) { 121 | var text = cm.getLine(i), pos = 0; 122 | for (;;) { 123 | var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos); 124 | if (nextOpen < 0) nextOpen = text.length; 125 | if (nextClose < 0) nextClose = text.length; 126 | pos = Math.min(nextOpen, nextClose); 127 | if (pos == text.length) break; 128 | if (cm.getTokenAt({line: i, ch: pos + 1}).type == tokenType) { 129 | if (pos == nextOpen) ++count; 130 | else if (!--count) { end = i; endCh = pos; break outer; } 131 | } 132 | ++pos; 133 | } 134 | } 135 | if (end == null || end == line + 1) return; 136 | return {from: {line: line, ch: startChar + 1}, 137 | to: {line: end, ch: endCh}}; 138 | }; 139 | 140 | CodeMirror.indentRangeFinder = function(cm, start) { 141 | var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); 142 | var myIndent = CodeMirror.countColumn(firstLine, null, tabSize); 143 | for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) { 144 | var curLine = cm.getLine(i); 145 | if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent) 146 | return {from: {line: start.line, ch: firstLine.length}, 147 | to: {line: i, ch: curLine.length}}; 148 | } 149 | }; 150 | 151 | CodeMirror.newFoldFunction = function(rangeFinder, widget) { 152 | if (widget == null) widget = "\u2194"; 153 | if (typeof widget == "string") { 154 | var text = document.createTextNode(widget); 155 | widget = document.createElement("span"); 156 | widget.appendChild(text); 157 | widget.className = "CodeMirror-foldmarker"; 158 | } 159 | 160 | return function(cm, pos) { 161 | if (typeof pos == "number") pos = {line: pos, ch: 0}; 162 | var range = rangeFinder(cm, pos); 163 | if (!range) return; 164 | 165 | var present = cm.findMarksAt(range.from), cleared = 0; 166 | for (var i = 0; i < present.length; ++i) { 167 | if (present[i].__isFold) { 168 | ++cleared; 169 | present[i].clear(); 170 | } 171 | } 172 | if (cleared) return; 173 | 174 | var myWidget = widget.cloneNode(true); 175 | CodeMirror.on(myWidget, "mousedown", function() {myRange.clear();}); 176 | var myRange = cm.markText(range.from, range.to, { 177 | replacedWith: myWidget, 178 | clearOnEnter: true, 179 | __isFold: true 180 | }); 181 | }; 182 | }; 183 | -------------------------------------------------------------------------------- /reports/assets/css/vendor/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | } 8 | .CodeMirror-scroll { 9 | /* Set scrolling behaviour here */ 10 | overflow: auto; 11 | } 12 | 13 | /* PADDING */ 14 | 15 | .CodeMirror-lines { 16 | padding: 4px 0; /* Vertical padding around content */ 17 | } 18 | .CodeMirror pre { 19 | padding: 0 4px; /* Horizontal padding of content */ 20 | } 21 | 22 | .CodeMirror-scrollbar-filler { 23 | background-color: white; /* The little square between H and V scrollbars */ 24 | } 25 | 26 | /* GUTTER */ 27 | 28 | .CodeMirror-gutters { 29 | border-right: 1px solid #ddd; 30 | background-color: #f7f7f7; 31 | } 32 | .CodeMirror-linenumbers {} 33 | .CodeMirror-linenumber { 34 | padding: 0 3px 0 5px; 35 | min-width: 20px; 36 | text-align: right; 37 | color: #999; 38 | } 39 | 40 | /* CURSOR */ 41 | 42 | .CodeMirror pre.CodeMirror-cursor { 43 | border-left: 1px solid black; 44 | } 45 | /* Shown when moving in bi-directional text */ 46 | .CodeMirror pre.CodeMirror-secondarycursor { 47 | border-left: 1px solid silver; 48 | } 49 | .cm-keymap-fat-cursor pre.CodeMirror-cursor { 50 | width: auto; 51 | border: 0; 52 | background: transparent; 53 | background: rgba(0, 200, 0, .4); 54 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800); 55 | } 56 | /* Kludge to turn off filter in ie9+, which also accepts rgba */ 57 | .cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) { 58 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 59 | } 60 | /* Can style cursor different in overwrite (non-insert) mode */ 61 | .CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {} 62 | 63 | /* DEFAULT THEME */ 64 | 65 | .cm-s-default .cm-keyword {color: #708;} 66 | .cm-s-default .cm-atom {color: #219;} 67 | .cm-s-default .cm-number {color: #164;} 68 | .cm-s-default .cm-def {color: #00f;} 69 | .cm-s-default .cm-variable {color: black;} 70 | .cm-s-default .cm-variable-2 {color: #05a;} 71 | .cm-s-default .cm-variable-3 {color: #085;} 72 | .cm-s-default .cm-property {color: black;} 73 | .cm-s-default .cm-operator {color: black;} 74 | .cm-s-default .cm-comment {color: #a50;} 75 | .cm-s-default .cm-string {color: #a11;} 76 | .cm-s-default .cm-string-2 {color: #f50;} 77 | .cm-s-default .cm-meta {color: #555;} 78 | .cm-s-default .cm-error {color: #f00;} 79 | .cm-s-default .cm-qualifier {color: #555;} 80 | .cm-s-default .cm-builtin {color: #30a;} 81 | .cm-s-default .cm-bracket {color: #997;} 82 | .cm-s-default .cm-tag {color: #170;} 83 | .cm-s-default .cm-attribute {color: #00c;} 84 | .cm-s-default .cm-header {color: blue;} 85 | .cm-s-default .cm-quote {color: #090;} 86 | .cm-s-default .cm-hr {color: #999;} 87 | .cm-s-default .cm-link {color: #00c;} 88 | 89 | .cm-negative {color: #d44;} 90 | .cm-positive {color: #292;} 91 | .cm-header, .cm-strong {font-weight: bold;} 92 | .cm-em {font-style: italic;} 93 | .cm-emstrong {font-style: italic; font-weight: bold;} 94 | .cm-link {text-decoration: underline;} 95 | 96 | .cm-invalidchar {color: #f00;} 97 | 98 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 99 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 100 | 101 | /* STOP */ 102 | 103 | /* The rest of this file contains styles related to the mechanics of 104 | the editor. You probably shouldn't touch them. */ 105 | 106 | .CodeMirror { 107 | line-height: 1; 108 | position: relative; 109 | overflow: hidden; 110 | } 111 | 112 | .CodeMirror-scroll { 113 | /* 30px is the magic margin used to hide the element's real scrollbars */ 114 | /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */ 115 | margin-bottom: -30px; margin-right: -30px; 116 | padding-bottom: 30px; padding-right: 30px; 117 | height: 100%; 118 | outline: none; /* Prevent dragging from highlighting the element */ 119 | position: relative; 120 | } 121 | .CodeMirror-sizer { 122 | position: relative; 123 | } 124 | 125 | /* The fake, visible scrollbars. Used to force redraw during scrolling 126 | before actuall scrolling happens, thus preventing shaking and 127 | flickering artifacts. */ 128 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler { 129 | position: absolute; 130 | z-index: 6; 131 | display: none; 132 | } 133 | .CodeMirror-vscrollbar { 134 | right: 0; top: 0; 135 | overflow-x: hidden; 136 | overflow-y: scroll; 137 | } 138 | .CodeMirror-hscrollbar { 139 | bottom: 0; left: 0; 140 | overflow-y: hidden; 141 | overflow-x: scroll; 142 | } 143 | .CodeMirror-scrollbar-filler { 144 | right: 0; bottom: 0; 145 | z-index: 6; 146 | } 147 | 148 | .CodeMirror-gutters { 149 | position: absolute; left: 0; top: 0; 150 | height: 100%; 151 | z-index: 3; 152 | } 153 | .CodeMirror-gutter { 154 | height: 100%; 155 | display: inline-block; 156 | /* Hack to make IE7 behave */ 157 | *zoom:1; 158 | *display:inline; 159 | } 160 | .CodeMirror-gutter-elt { 161 | position: absolute; 162 | cursor: default; 163 | z-index: 4; 164 | } 165 | 166 | .CodeMirror-lines { 167 | cursor: text; 168 | } 169 | .CodeMirror pre { 170 | /* Reset some styles that the rest of the page might have set */ 171 | -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0; 172 | border-width: 0; 173 | background: transparent; 174 | font-family: inherit; 175 | font-size: inherit; 176 | margin: 0; 177 | white-space: pre; 178 | word-wrap: normal; 179 | line-height: inherit; 180 | color: inherit; 181 | z-index: 2; 182 | position: relative; 183 | overflow: visible; 184 | } 185 | .CodeMirror-wrap pre { 186 | word-wrap: break-word; 187 | white-space: pre-wrap; 188 | word-break: normal; 189 | } 190 | .CodeMirror-linebackground { 191 | position: absolute; 192 | left: 0; right: 0; top: 0; bottom: 0; 193 | z-index: 0; 194 | } 195 | 196 | .CodeMirror-linewidget { 197 | position: relative; 198 | z-index: 2; 199 | } 200 | 201 | .CodeMirror-wrap .CodeMirror-scroll { 202 | overflow-x: hidden; 203 | } 204 | 205 | .CodeMirror-measure { 206 | position: absolute; 207 | width: 100%; height: 0px; 208 | overflow: hidden; 209 | visibility: hidden; 210 | } 211 | .CodeMirror-measure pre { position: static; } 212 | 213 | .CodeMirror pre.CodeMirror-cursor { 214 | position: absolute; 215 | visibility: hidden; 216 | border-right: none; 217 | width: 0; 218 | } 219 | .CodeMirror-focused pre.CodeMirror-cursor { 220 | visibility: visible; 221 | } 222 | 223 | .CodeMirror-selected { background: #d9d9d9; } 224 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 225 | 226 | .CodeMirror-searching { 227 | background: #ffa; 228 | background: rgba(255, 255, 0, .4); 229 | } 230 | 231 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 232 | .CodeMirror span { *vertical-align: text-bottom; } 233 | 234 | @media print { 235 | /* Hide the cursor when printing */ 236 | .CodeMirror pre.CodeMirror-cursor { 237 | visibility: hidden; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /reports/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plato - JavaScript Introspection 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 |
      41 |
      42 |

      JavaScript Source Analysis

      43 |
      44 |
      45 | 46 |
      47 |
      48 |

      Summary

      49 |
      50 |
      51 |
      52 |

      Total/Average Lines

      53 |

      532 / 266

      54 |
      55 |
      56 |

      Average Maintainability

      57 |

      69.73

      58 |
      59 |
      60 |
      61 |
      62 |
      63 |
      64 |
      65 |
      66 |
      67 |
      68 |
      69 |
      70 |
      71 | 72 | 73 |
      74 |
      75 |

      Maintainability

      76 |
      77 |
      78 |
      79 |

      Lines of code

      80 |
      81 |
      82 |
      83 |

      Estimated errors in implementation

      84 |
      85 |
      86 | 87 |
      88 |

      Lint errors

      89 |
      90 |
      91 | 92 |
      93 | 94 |
      95 |
      96 |

      Files

      97 |
      98 | 99 |
      100 |
      101 |
      102 | 103 | 104 | 105 | 106 |
      107 |
      108 |
      109 | 110 |
      111 |   112 |
      113 | 114 |
      115 |
        116 | 117 |
      • 118 |
        119 | 120 | amd.js 121 | 127 |
        128 |
      • 129 | 130 |
      • 131 |
        132 | 133 | backbone.picky.js 134 | 140 |
        141 |
      • 142 | 143 |
      144 |
      145 |
      146 | 147 | 148 |
      149 |
      150 |

      .

      151 |
      152 |
      153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /spec/javascripts/multiSelect.spec.js: -------------------------------------------------------------------------------- 1 | describe("multi-select collection: general", function(){ 2 | var Model = Backbone.Model.extend({ 3 | initialize: function(){ 4 | Backbone.Picky.Selectable.applyTo(this); 5 | } 6 | }); 7 | 8 | var Collection = Backbone.Collection.extend({ 9 | model: Model, 10 | 11 | initialize: function(){ 12 | Backbone.Picky.MultiSelect.applyTo(this); 13 | } 14 | }); 15 | 16 | describe('automatic invocation of onSelectNone, onSelectSome, onSelectAll, onReselect handlers', function () { 17 | var EventHandlingCollection, model, collection; 18 | 19 | beforeEach(function () { 20 | 21 | EventHandlingCollection = Collection.extend({ 22 | onSelectNone: function () {}, 23 | onSelectSome: function () {}, 24 | onSelectAll: function () {}, 25 | onReselect: function () {} 26 | }); 27 | 28 | model = new Model(); 29 | collection = new EventHandlingCollection([model]); 30 | 31 | spyOn(collection, "onSelectNone").andCallThrough(); 32 | spyOn(collection, "onSelectSome").andCallThrough(); 33 | spyOn(collection, "onSelectAll").andCallThrough(); 34 | spyOn(collection, "onReselect").andCallThrough(); 35 | }); 36 | 37 | it('calls the onSelectNone handler when triggering a select:none event', function () { 38 | collection.trigger("select:none", { selected: [], deselected: [model] }, collection, {foo: "bar"}); 39 | expect(collection.onSelectNone).toHaveBeenCalledWith({ selected: [], deselected: [model] }, collection, {foo: "bar"}); 40 | }); 41 | 42 | it('calls the onSelectSome handler when triggering a select:some event', function () { 43 | collection.trigger("select:some", { selected: [model], deselected: [] }, collection, {foo: "bar"}); 44 | expect(collection.onSelectSome).toHaveBeenCalledWith({ selected: [model], deselected: [] }, collection, {foo: "bar"}); 45 | }); 46 | 47 | it('calls the onSelectAll handler when triggering a select:all event', function () { 48 | collection.trigger("select:all", { selected: [model], deselected: [] }, collection, {foo: "bar"}); 49 | expect(collection.onSelectAll).toHaveBeenCalledWith({ selected: [model], deselected: [] }, collection, {foo: "bar"}); 50 | }); 51 | 52 | it('calls the onReselect handler when triggering a reselect:any event', function () { 53 | collection.trigger("reselect:any", [model], collection, {foo: "bar"}); 54 | expect(collection.onReselect).toHaveBeenCalledWith([model], collection, {foo: "bar"}); 55 | }); 56 | }); 57 | 58 | describe('Model-sharing status flag', function () { 59 | 60 | describe('when model sharing is disabled', function () { 61 | var collection; 62 | 63 | it('with no models being passed in during construction, the _modelSharingEnabled property is not set to true', function () { 64 | // Ie, the property must not exist, or be false. 65 | collection = new Collection(); 66 | expect(collection._modelSharingEnabled).not.toBe(true); 67 | }); 68 | 69 | it('with models being passed in during construction, the _modelSharingEnabled property is not set to true', function () { 70 | // Ie, the property must not exist, or be false. 71 | collection = new Collection( [new Model()]); 72 | expect(collection._modelSharingEnabled).not.toBe(true); 73 | }); 74 | }); 75 | 76 | describe('when model sharing is enabled', function () { 77 | var SharingCollection, collection; 78 | 79 | beforeEach(function () { 80 | SharingCollection = Backbone.Collection.extend({ 81 | initialize: function(models){ 82 | Backbone.Picky.MultiSelect.applyTo(this, models); 83 | } 84 | }); 85 | }); 86 | 87 | it('with no models being passed in during construction, the _modelSharingEnabled property is true', function () { 88 | collection = new SharingCollection(); 89 | expect(collection._modelSharingEnabled).toBe(true); 90 | }); 91 | 92 | it('with models being passed in during construction, the _modelSharingEnabled property is true', function () { 93 | collection = new SharingCollection( [new Model()]); 94 | expect(collection._modelSharingEnabled).toBe(true); 95 | }); 96 | }); 97 | 98 | }); 99 | 100 | describe('Checking for memory leaks', function () { 101 | 102 | describe('when a collection is replaced by another one and is not referenced by a variable any more, with model sharing disabled', function () { 103 | var logger, LoggedCollection, m1, m2, collection; 104 | 105 | beforeEach(function () { 106 | logger = new Logger(); 107 | 108 | LoggedCollection = Collection.extend({ 109 | initialize: function(models){ 110 | this.on("select:none", function () { 111 | logger.log( "select:none event fired in selected in collection " + this._pickyCid ); 112 | }); 113 | this.on("select:some", function () { 114 | logger.log( "select:some event fired in selected in collection " + this._pickyCid ); 115 | }); 116 | this.on("select:all", function () { 117 | logger.log( "select:all event fired in selected in collection " + this._pickyCid ); 118 | }); 119 | 120 | Collection.prototype.initialize.call(this, models); 121 | } 122 | }); 123 | 124 | m1 = new Model(); 125 | m2 = new Model(); 126 | }); 127 | 128 | it('should no longer respond to model events', function () { 129 | // With only variable holding a collection, only one 'select:*' event 130 | // should be logged. 131 | 132 | //noinspection JSUnusedAssignment 133 | collection = new LoggedCollection([m1, m2]); 134 | collection = new LoggedCollection([m1, m2]); 135 | 136 | m2.select(); 137 | expect(logger.entries.length).toBe(1); 138 | }); 139 | }); 140 | 141 | describe('when a collection is replaced by another one and is not referenced by a variable any more, with model sharing enabled', function () { 142 | var logger, Collection, LoggedCollection, m1, m2, collection; 143 | 144 | beforeEach(function () { 145 | logger = new Logger(); 146 | 147 | Collection = Backbone.Collection.extend({ 148 | model: Model, 149 | 150 | initialize: function(models){ 151 | Backbone.Picky.MultiSelect.applyTo(this, models); 152 | } 153 | }); 154 | 155 | LoggedCollection = Collection.extend({ 156 | initialize: function(models){ 157 | this.on("select:none", function () { 158 | logger.log( "select:none event fired in selected in collection " + this._pickyCid ); 159 | }); 160 | this.on("select:some", function () { 161 | logger.log( "select:some event fired in selected in collection " + this._pickyCid ); 162 | }); 163 | this.on("select:all", function () { 164 | logger.log( "select:all event fired in selected in collection " + this._pickyCid ); 165 | }); 166 | 167 | Collection.prototype.initialize.call(this, models); 168 | } 169 | }); 170 | 171 | m1 = new Model(); 172 | m2 = new Model(); 173 | }); 174 | 175 | it('should no longer respond to model events after calling close on it', function () { 176 | // With only variable holding a collection, only one 'select:*' event 177 | // should be logged. 178 | collection = new LoggedCollection([m1, m2]); 179 | collection.close(); 180 | collection = new LoggedCollection([m1, m2]); 181 | 182 | m2.select(); 183 | expect(logger.entries.length).toBe(1); 184 | }); 185 | }); 186 | 187 | }); 188 | 189 | }); 190 | -------------------------------------------------------------------------------- /spec/javascripts/multiSelect.toggleSelectAll.spec.js: -------------------------------------------------------------------------------- 1 | describe("multi-select collection: toggleSelectAll", function(){ 2 | var Model = Backbone.Model.extend({ 3 | initialize: function(){ 4 | Backbone.Picky.Selectable.applyTo(this); 5 | } 6 | }); 7 | 8 | var Collection = Backbone.Collection.extend({ 9 | model: Model, 10 | 11 | initialize: function(){ 12 | Backbone.Picky.MultiSelect.applyTo(this); 13 | } 14 | }); 15 | 16 | describe("when no models are selected, and toggling", function(){ 17 | var m1, m2, collection; 18 | 19 | beforeEach(function(){ 20 | m1 = new Model(); 21 | m2 = new Model(); 22 | 23 | collection = new Collection([m1, m2]); 24 | spyOn(collection, "trigger").andCallThrough(); 25 | 26 | collection.toggleSelectAll(); 27 | }); 28 | 29 | it("should trigger a select:all event", function(){ 30 | expect(collection.trigger).toHaveBeenCalledWithInitial("select:all", { selected: [m1, m2], deselected: [] }, collection); 31 | }); 32 | 33 | it("should have a selected count of 2", function(){ 34 | expect(collection.selectedLength).toBe(2); 35 | }); 36 | 37 | it("should have 2 models in the selected list", function(){ 38 | var size = _.size(collection.selected); 39 | expect(size).toBe(2); 40 | }); 41 | }); 42 | 43 | describe("when no models are selected, and toggling with options.silent enabled", function(){ 44 | var m1, m2, collection; 45 | 46 | beforeEach(function(){ 47 | m1 = new Model(); 48 | m2 = new Model(); 49 | 50 | collection = new Collection([m1, m2]); 51 | spyOn(collection, "trigger").andCallThrough(); 52 | 53 | collection.toggleSelectAll({silent: true}); 54 | }); 55 | 56 | it("should not trigger a select:all event", function(){ 57 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("select:all"); 58 | }); 59 | 60 | it("should have a selected count of 2", function(){ 61 | expect(collection.selectedLength).toBe(2); 62 | }); 63 | 64 | it("should have 2 models in the selected list", function(){ 65 | var size = _.size(collection.selected); 66 | expect(size).toBe(2); 67 | }); 68 | }); 69 | 70 | describe("when some models are selected, and toggling", function(){ 71 | var m1, m2, collection; 72 | 73 | beforeEach(function(){ 74 | m1 = new Model(); 75 | m2 = new Model(); 76 | 77 | collection = new Collection([m1, m2]); 78 | m1.select(); 79 | 80 | spyOn(collection, "trigger").andCallThrough(); 81 | 82 | collection.toggleSelectAll(); 83 | }); 84 | 85 | it("should trigger a select:all event", function(){ 86 | expect(collection.trigger).toHaveBeenCalledWithInitial("select:all", { selected: [m2], deselected: [] }, collection); 87 | }); 88 | 89 | it("should trigger a reselect:any event", function(){ 90 | expect(collection.trigger).toHaveBeenCalledWithInitial("reselect:any", [m1], collection); 91 | }); 92 | 93 | it("should have a selected count of 2", function(){ 94 | expect(collection.selectedLength).toBe(2); 95 | }); 96 | 97 | it("should have 2 models in the selected list", function(){ 98 | var size = _.size(collection.selected); 99 | expect(size).toBe(2); 100 | }); 101 | }); 102 | 103 | describe("when all models are selected, and toggling", function(){ 104 | var m1, m2, collection; 105 | 106 | beforeEach(function(){ 107 | m1 = new Model(); 108 | m2 = new Model(); 109 | 110 | collection = new Collection([m1, m2]); 111 | m1.select(); 112 | m2.select(); 113 | 114 | spyOn(collection, "trigger").andCallThrough(); 115 | 116 | collection.toggleSelectAll(); 117 | }); 118 | 119 | it("should trigger a select:none event", function(){ 120 | expect(collection.trigger).toHaveBeenCalledWithInitial("select:none", { selected: [], deselected: [m1, m2] }, collection); 121 | }); 122 | 123 | it("should have a selected count of 0", function(){ 124 | expect(collection.selectedLength).toBe(0); 125 | }); 126 | 127 | it("should have 0 models in the selected list", function(){ 128 | var size = _.size(collection.selected); 129 | expect(size).toBe(0); 130 | }); 131 | }); 132 | 133 | describe("when all models are selected, and toggling with options.silent enabled", function(){ 134 | var m1, m2, collection; 135 | 136 | beforeEach(function(){ 137 | m1 = new Model(); 138 | m2 = new Model(); 139 | 140 | collection = new Collection([m1, m2]); 141 | m1.select(); 142 | m2.select(); 143 | 144 | spyOn(collection, "trigger").andCallThrough(); 145 | 146 | collection.toggleSelectAll({silent: true}); 147 | }); 148 | 149 | it("should not trigger a select:none event", function(){ 150 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("select:none"); 151 | }); 152 | 153 | it("should have a selected count of 0", function(){ 154 | expect(collection.selectedLength).toBe(0); 155 | }); 156 | 157 | it("should have 0 models in the selected list", function(){ 158 | var size = _.size(collection.selected); 159 | expect(size).toBe(0); 160 | }); 161 | }); 162 | 163 | describe('custom options', function () { 164 | 165 | describe("when no models are selected, and toggling with a custom option", function(){ 166 | var m1, m2, collection; 167 | 168 | beforeEach(function(){ 169 | m1 = new Model(); 170 | m2 = new Model(); 171 | 172 | collection = new Collection([m1, m2]); 173 | 174 | spyOn(m1, "trigger").andCallThrough(); 175 | spyOn(m2, "trigger").andCallThrough(); 176 | spyOn(collection, "trigger").andCallThrough(); 177 | 178 | collection.toggleSelectAll({foo: "bar"}); 179 | }); 180 | 181 | it("should trigger a selected event on the first model and pass the options object along as the last parameter", function(){ 182 | expect(m1.trigger).toHaveBeenCalledWith("selected", m1, {foo: "bar"}); 183 | }); 184 | 185 | it("should trigger a selected event on the second model and pass the options object along as the last parameter", function(){ 186 | expect(m2.trigger).toHaveBeenCalledWith("selected", m2, {foo: "bar"}); 187 | }); 188 | 189 | it("should trigger a select:all event and pass the options object along as the last parameter", function(){ 190 | expect(collection.trigger).toHaveBeenCalledWith("select:all", { selected: [m1, m2], deselected: [] }, collection, {foo: "bar"}); 191 | }); 192 | }); 193 | 194 | describe("when all models are selected, and toggling with a custom option", function(){ 195 | var m1, m2, collection; 196 | 197 | beforeEach(function(){ 198 | m1 = new Model(); 199 | m2 = new Model(); 200 | 201 | collection = new Collection([m1, m2]); 202 | collection.selectAll(); 203 | 204 | spyOn(m1, "trigger").andCallThrough(); 205 | spyOn(m2, "trigger").andCallThrough(); 206 | spyOn(collection, "trigger").andCallThrough(); 207 | 208 | collection.toggleSelectAll({foo: "bar"}); 209 | }); 210 | 211 | it("should trigger a deselected event on the first model and pass the options object along as the last parameter", function(){ 212 | expect(m1.trigger).toHaveBeenCalledWith("deselected", m1, {foo: "bar"}); 213 | }); 214 | 215 | it("should trigger a deselected event on the second model and pass the options object along as the last parameter", function(){ 216 | expect(m2.trigger).toHaveBeenCalledWith("deselected", m2, {foo: "bar"}); 217 | }); 218 | 219 | it("should trigger a select:none event and pass the options object along as the last parameter", function(){ 220 | expect(collection.trigger).toHaveBeenCalledWith("select:none", { selected: [], deselected: [m1, m2] }, collection, {foo: "bar"}); 221 | }); 222 | }); 223 | 224 | }); 225 | 226 | }); 227 | -------------------------------------------------------------------------------- /lib/backbone.picky.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"lib/backbone.picky.min.js","sources":["backbone.picky.js"],"names":["Backbone","Picky","_","onAdd","model","collection","registerCollectionWithModel","selected","select","_silentReselect","_externalEvent","onRemove","options","releaseModel","extend","_pickyCollections","without","_pickyCid","length","deselect","_skipModelCall","onResetSingleSelect","excessiveSelections","deselectOnRemove","find","previousModels","_silentLocally","filter","initial","each","last","silent","onResetMultiSelect","push","unregisterCollectionWithModels","stripLocalOptions","omit","stripInternalOptions","multiSelectionToArray","selectionsHash","mapper","value","key","selectedArr","trigger","context","getEventName","match","prefix","eventName","toUpperCase","unifyEventNames","slice","origTrigger","splitter","event","unifiedEvent","methodName","replace","method","this","isFunction","apply","tail","arguments","SingleSelect","models","uniqueId","listenTo","_modelSharingEnabled","prototype","reselected","undefined","_processedBy","cid","close","stopListening","MultiSelect","prevSelected","selectedLength","size","triggerMultiSelectEvents","selectAll","deselectAll","selectNone","toggleSelectAll","Selectable","toggleSelected","applyTo","hostObject","singleSelect","multiSelect","mapCidsToModels","cids","previousSelection","get","map","diff","prevSelectedCids","keys","selectedCids","addedCids","difference","removedCids","unchanged","deselected"],"mappings":";;;;;;AAKAA,SAASC,MAAQ,SAAWD,EAAUE,GAgYpC,QAASC,GAAOC,EAAOC,GACrBC,EAA4BF,EAAOC,GAC/BD,EAAMG,UAAUF,EAAWG,OAAOJ,GAAQK,iBAAiB,EAAMC,eAAgB,QAGvF,QAASC,GAAUP,EAAOC,EAAYO,GACpCC,EAAaT,EAAOC,EAAYH,EAAEY,UAAWF,GAAUF,eAAgB,YAGzE,QAASG,GAAcT,EAAOC,EAAYO,GACpCR,EAAMW,oBAAmBX,EAAMW,kBAAoBb,EAAEc,QAAQZ,EAAMW,kBAAmBV,EAAWY,YACjGb,EAAMG,WACJH,EAAMW,mBAAwD,IAAnCX,EAAMW,kBAAkBG,OACrDb,EAAWc,SAASf,EAAOQ,GAE3BP,EAAWc,SAASf,EAAOF,EAAEY,UAAWF,GAAUQ,gBAAgB,MAKxE,QAASC,GAAqBhB,EAAYO,GACxC,GAAIL,GACAe,EACAC,EAAmBrB,EAAEsB,KAAKZ,EAAQa,eAAgB,SAAUrB,GAAS,MAAOA,GAAMG,UAElFgB,IAAkBV,EAAaU,EAAkBlB,GAAaqB,gBAAgB,IAElFnB,EAAWF,EAAWsB,OAAO,SAAUvB,GAAS,MAAOA,GAAMG,WAC7De,EAAsBpB,EAAE0B,QAAQrB,GAC5Be,EAAoBJ,QAAQhB,EAAE2B,KAAKP,EAAqB,SAAUlB,GAASA,EAAMe,aACjFZ,EAASW,QAAQb,EAAWG,OAAON,EAAE4B,KAAKvB,IAAYwB,QAAQ,IAGpE,QAASC,GAAoB3B,EAAYO,GACvC,GAAIJ,GACAW,EAAWjB,EAAEyB,OAAOf,EAAQa,eAAgB,SAAUrB,GAAS,MAAOA,GAAMG,UAE5EY,IAAUjB,EAAE2B,KAAKV,EAAU,SAAUf,GAASS,EAAaT,EAAOC,GAAaqB,gBAAgB,MAEnGlB,EAASH,EAAWsB,OAAO,SAAUvB,GAAS,MAAOA,GAAMG,WACvDC,EAAOU,QAAQhB,EAAE2B,KAAKrB,EAAQ,SAAUJ,GAASC,EAAWG,OAAOJ,GAAQ2B,QAAQ,MAGzF,QAASzB,GAA6BF,EAAOC,GAC3CD,EAAMW,oBAAsBX,EAAMW,sBAClCX,EAAMW,kBAAkBkB,KAAK5B,EAAWY,WAG1C,QAASiB,GAAgC7B,GACvCA,EAAWwB,KAAK,SAAUzB,GACxBS,EAAaT,EAAOC,GAAaqB,gBAAgB,MAIrD,QAASS,GAAmBvB,GAC1B,MAAOV,GAAEkC,KAAKxB,EAAS,iBAAkB,kBAG3C,QAASyB,GAAsBzB,GAC7B,MAAOV,GAAEkC,KAAKxB,EAAS,iBAAkB,kBAAmB,iBAAkB,gBAGhF,QAAS0B,GAAuBC,GAC9B,QAASC,GAAQC,EAAOC,GACtBC,EAAYD,GAAOD,EAGrB,GAAIE,KAGJ,OAFAzC,GAAE2B,KAAKU,EAAgBC,GAEhBG,EAOT,QAASC,GAASC,GAKhB,QAASC,GAAcC,EAAOC,EAAQC,GACpC,MAAOA,GAAUC,cAOnB,QAASC,GAAiBF,GAOxB,MAN4B,OAAxBA,EAAUG,MAAM,IAClBH,EAAYA,EAAUG,MAAM,EAAG,KACE,SAAxBH,EAAUG,MAAM,KAA0C,SAAxBH,EAAUG,MAAM,OAC3DH,EAAYA,EAAUG,MAAM,EAAG,KAG1BH,EAGT,GAAII,GAAcR,EAAQD,QAGtBU,EAAW,aAIf,OAAO,UAAUC,GAEf,GAAIC,GAAeL,EAAgBI,GAC/BE,EAAa,KAAOD,EAAaE,QAAQJ,EAAUR,GACnDa,EAASC,KAAKH,EAUlB,OAPIvD,GAAE2D,WAAWF,IAEfA,EAAOG,MAAMF,KAAM1D,EAAE6D,KAAKC,YAI5BX,EAAYS,MAAMF,KAAMI,WACjBJ,MAxfX,GAAI3D,KASJA,GAAMgE,aAAe,SAAS5D,EAAY6D,GACxCN,KAAK3C,UAAYf,EAAEiE,SAAS,gBAC5BP,KAAKvD,WAAaA,EAClBuD,KAAKhB,QAAUA,EAAQvC,GAEnB2D,UAAU9C,OAAS,IAGrBhB,EAAE2B,KAAKqC,MAAc,SAAU9D,GAC7BE,EAA4BF,EAAOwD,MAC/BxD,EAAMG,WACJqD,KAAKrD,UAAUqD,KAAKrD,SAASY,WACjCyC,KAAKrD,SAAWH,IAEjBwD,MAEHA,KAAKvD,WAAW+D,SAASR,KAAKvD,WAAY,YAAauD,KAAKpD,QAC5DoD,KAAKvD,WAAW+D,SAASR,KAAKvD,WAAY,cAAeuD,KAAKzC,UAE9DyC,KAAKvD,WAAW+D,SAASR,KAAKvD,WAAY,QAASgB,GACnDuC,KAAKvD,WAAW+D,SAASR,KAAKvD,WAAY,MAAOF,GACjDyD,KAAKvD,WAAW+D,SAASR,KAAKvD,WAAY,SAAUM,GAIpDiD,KAAKS,sBAAuB,IAMhCnE,EAAEY,OAAOb,EAAMgE,aAAaK,WAI1B9D,OAAQ,SAASJ,EAAOQ,GACtB,GAAI2D,GAAanE,GAASwD,KAAKrD,WAAaH,EAAQA,EAAQoE,MAE5D5D,KAAYA,MACZA,EAAQ6D,eAAiB7D,EAAQ6D,iBAC7B7D,EAAQ6D,aAAab,KAAK3C,aAEzBsD,IACHX,KAAKzC,SAASqD,OAAWtE,EAAEkC,KAAKxB,EAAS,mBACzCgD,KAAKrD,SAAWH,GAElBQ,EAAQ6D,aAAab,KAAK3C,WAAa2C,KAElChD,EAAQ6D,aAAab,KAAKrD,SAASmE,MAAMd,KAAKrD,SAASC,OAAO2B,EAAkBvB,IAE/EA,EAAQmB,QAAUnB,EAAQc,iBAE1B6C,EACG3D,EAAQH,iBAAiBmD,KAAKhB,QAAQ,eAAgBxC,EAAOwD,KAAMvB,EAAqBzB,IAE7FgD,KAAKhB,QAAQ,aAAcxC,EAAOwD,KAAMvB,EAAqBzB,OAQnEO,SAAU,SAASf,EAAOQ,GACxBA,IAAYA,MACPgD,KAAKrD,WAEVH,EAAQA,GAASwD,KAAKrD,SAClBqD,KAAKrD,WAAaH,UAEfwD,MAAKrD,SACPK,EAAQQ,gBAAgBhB,EAAMe,SAASgB,EAAkBvB,IACxDA,EAAQmB,QAAUnB,EAAQc,gBAAiBkC,KAAKhB,QAAQ,eAAgBxC,EAAOwD,KAAMvB,EAAqBzB,OAGlH+D,MAAO,WACLzC,EAA+B0B,MAC/BA,KAAKgB,mBAWT3E,EAAM4E,YAAc,SAAUxE,EAAY6D,GACxCN,KAAK3C,UAAYf,EAAEiE,SAAS,eAC5BP,KAAKvD,WAAaA,EAClBuD,KAAKrD,YACLqD,KAAKhB,QAAUA,EAAQvC,GAEnB2D,UAAU9C,OAAS,IAGrBhB,EAAE2B,KAAKqC,MAAc,SAAU9D,GAC7BE,EAA4BF,EAAOwD,MAC/BxD,EAAMG,WAAUqD,KAAKrD,SAASH,EAAMsE,KAAOtE,IAC9CwD,MAEHA,KAAKvD,WAAW+D,SAASR,KAAKvD,WAAY,YAAauD,KAAKpD,QAC5DoD,KAAKvD,WAAW+D,SAASR,KAAKvD,WAAY,cAAeuD,KAAKzC,UAE9DyC,KAAKvD,WAAW+D,SAASR,KAAKvD,WAAY,QAAS2B,GACnD4B,KAAKvD,WAAW+D,SAASR,KAAKvD,WAAY,MAAOF,GACjDyD,KAAKvD,WAAW+D,SAASR,KAAKvD,WAAY,SAAUM,GAIpDiD,KAAKS,sBAAuB,IAMhCnE,EAAEY,OAAOb,EAAM4E,YAAYP,WAKzB9D,OAAQ,SAAUJ,EAAOQ,GACvB,GAAIkE,GAAexC,EAAsBsB,KAAKrD,UAC1CgE,EAAaX,KAAKrD,SAASH,EAAMsE,MAAStE,KAE9CQ,KAAYA,MACZA,EAAQ6D,eAAiB7D,EAAQ6D,iBAE7BF,EAAWrD,QAAUN,EAAQ6D,aAAab,KAAK3C,aAE9CsD,EAAWrD,SACd0C,KAAKrD,SAASH,EAAMsE,KAAOtE,EAC3BwD,KAAKmB,eAAiB7E,EAAE8E,KAAKpB,KAAKrD,WAEpCK,EAAQ6D,aAAab,KAAK3C,WAAa2C,KAElChD,EAAQ6D,aAAarE,EAAMsE,MAAMtE,EAAMI,OAAO2B,EAAkBvB,IACrEqE,EAAyBrB,KAAMkB,EAAclE,EAAS2D,KAMxDpD,SAAU,SAAUf,EAAOQ,GACzB,GAAIkE,GAAexC,EAAsBsB,KAAKrD,SAE9CK,KAAYA,MACPgD,KAAKrD,SAASH,EAAMsE,aAElBd,MAAKrD,SAASH,EAAMsE,KAC3Bd,KAAKmB,eAAiB7E,EAAE8E,KAAKpB,KAAKrD,UAE7BK,EAAQQ,gBAAgBhB,EAAMe,SAASgB,EAAkBvB,IAC9DqE,EAAyBrB,KAAMkB,EAAclE,KAI/CsE,UAAW,SAAUtE,GACnB,GAAIkE,GAAexC,EAAsBsB,KAAKrD,UAC1CgE,IAEJ3D,KAAYA,MACZA,EAAQ6D,eAAiB7D,EAAQ6D,iBAEjCb,KAAKmB,eAAiB,EACtBnB,KAAK/B,KAAK,SAAUzB,GAClBwD,KAAKmB,iBACDnB,KAAKrD,SAASH,EAAMsE,MAAMH,EAAWtC,KAAK7B,GAC9CwD,KAAKpD,OAAOJ,EAAOF,EAAEY,UAAWF,GAAUc,gBAAgB,MACzDkC,MACHhD,EAAQ6D,aAAab,KAAK3C,WAAa2C,KAEvCqB,EAAyBrB,KAAMkB,EAAclE,EAAS2D,IAIxDY,YAAa,SAAUvE,GACrB,GAAIkE,EAEwB,KAAxBlB,KAAKmB,iBACTD,EAAexC,EAAsBsB,KAAKrD,UAE1CqD,KAAK/B,KAAK,SAAUzB,GACdA,EAAMG,UAAUqD,KAAKmB,iBACzBnB,KAAKzC,SAASf,EAAOF,EAAEY,UAAWF,GAAUc,gBAAgB,MAC3DkC,MAEHA,KAAKmB,eAAiB,EACtBE,EAAyBrB,KAAMkB,EAAclE,KAG/CwE,WAAY,SAAUxE,GACpBgD,KAAKuB,YAAYvE,IAMnByE,gBAAiB,SAAUzE,GACrBgD,KAAKmB,iBAAmBnB,KAAK1C,OAC/B0C,KAAKuB,YAAYvE,GAEjBgD,KAAKsB,UAAUtE,IAInB+D,MAAO,WACLzC,EAA+B0B,MAC/BA,KAAKgB,mBAST3E,EAAMqF,WAAa,SAAUlF,GAC3BwD,KAAKxD,MAAQA,EACbwD,KAAKhB,QAAUA,EAAQxC,IAGzBF,EAAEY,OAAOb,EAAMqF,WAAWhB,WAIxB9D,OAAQ,SAAUI,GAChB,GAAI2D,GAAaX,KAAKrD,QAEtBK,KAAYA,MACZA,EAAQ6D,eAAiB7D,EAAQ6D,iBAE7B7D,EAAQ6D,aAAab,KAAKc,OAE9Bd,KAAKrD,UAAW,EAChBK,EAAQ6D,aAAab,KAAKc,KAAOd,KAE7BA,KAAK7C,kBAEP6C,KAAKhB,QAAQ,YAAagB,KAAMzB,EAAkBvB,IACzCgD,KAAKvD,aAGTO,EAAQ6D,aAAab,KAAKvD,WAAWY,YAAY2C,KAAKvD,WAAWG,OAAOoD,KAAMzB,EAAkBvB,KAGjGA,EAAQmB,QAAUnB,EAAQc,iBAC1B6C,EACG3D,EAAQH,iBAAiBmD,KAAKhB,QAAQ,aAAcgB,KAAMvB,EAAqBzB,IAEpFgD,KAAKhB,QAAQ,WAAYgB,KAAMvB,EAAqBzB,OAO1DO,SAAU,SAAUP,GAClBA,IAAYA,MACPgD,KAAKrD,WAEVqD,KAAKrD,UAAW,EAEZqD,KAAK7C,kBAEP6C,KAAKhB,QAAQ,cAAegB,KAAMzB,EAAkBvB,IAC3CgD,KAAKvD,YAGduD,KAAKvD,WAAWc,SAASyC,KAAMzB,EAAkBvB,IAG7CA,EAAQmB,QAAUnB,EAAQc,gBAAiBkC,KAAKhB,QAAQ,aAAcgB,KAAMvB,EAAqBzB,MAKzG2E,eAAgB,SAAU3E,GACpBgD,KAAKrD,SACPqD,KAAKzC,SAASP,GAEdgD,KAAKpD,OAAOI,MAMlBX,EAAMqF,WAAWE,QAAU,SAAUC,GACnCvF,EAAEY,OAAO2E,EAAY,GAAIzF,GAASC,MAAMqF,WAAWG,KAGrDxF,EAAMgE,aAAauB,QAAU,SAAUC,EAAYvB,GACjD,GAAIwB,EAIFA,GAFE1B,UAAU9C,OAAS,EAEL,GAAIlB,GAASC,MAAMgE,aAAawB,GAGhC,GAAIzF,GAASC,MAAMgE,aAAawB,EAAYvB,GAG9DhE,EAAEY,OAAO2E,EAAYC,IAGvBzF,EAAM4E,YAAYW,QAAU,SAAUC,EAAYvB,GAChD,GAAIyB,EAIFA,GAFE3B,UAAU9C,OAAS,EAEN,GAAIlB,GAASC,MAAM4E,YAAYY,GAG/B,GAAIzF,GAASC,MAAM4E,YAAYY,EAAYvB,GAG5DhE,EAAEY,OAAO2E,EAAYE,GAQvB,IAAIV,GAA2B,SAAU5E,EAAYyE,EAAclE,EAAS2D,GAC1E,QAASqB,GAAiBC,EAAMxF,EAAYyF,GAC1C,QAAStD,GAAQkC,GAGf,MAAOrE,GAAW0F,IAAIrB,IAAQoB,EAAkBpB,GAElD,MAAOxE,GAAE8F,IAAIH,EAAMrD,GAIrB,GADA5B,IAAYA,OACRA,EAAQmB,SAAUnB,EAAQc,eAA9B,CAEA,GAOIuE,GAPAlB,EAAiB1E,EAAW0E,eAC5B7D,EAASb,EAAWa,OACpBgF,EAAmBhG,EAAEiG,KAAKrB,GAC1BsB,EAAelG,EAAEiG,KAAK9F,EAAWE,UACjC8F,EAAYnG,EAAEoG,WAAYF,EAAcF,GACxCK,EAAcrG,EAAEoG,WAAYJ,EAAkBE,GAC9CI,EAAazB,IAAmBmB,EAAiBhF,QAA+B,IAArBmF,EAAUnF,QAAuC,IAAvBqF,EAAYrF,MAOrG,IAJIqD,GAAcA,EAAWrD,SAAWN,EAAQH,iBAC9CJ,EAAWuC,QAAQ,eAAgB2B,EAAYlE,EAAYgC,EAAqBzB,KAG9E4F,EAOJ,MALAP,IACE1F,SAAUqF,EAAgBS,EAAWhG,EAAYyE,GACjD2B,WAAYb,EAAgBW,EAAalG,EAAYyE,IAGnDC,IAAmB7D,MACrBb,GAAWuC,QAAQ,aAAcqD,EAAM5F,EAAYgC,EAAqBzB,IAInD,IAAnBmE,MACF1E,GAAWuC,QAAQ,cAAeqD,EAAM5F,EAAYgC,EAAqBzB,IAIvEmE,EAAiB,GAAsB7D,EAAjB6D,MACxB1E,GAAWuC,QAAQ,cAAeqD,EAAM5F,EAAYgC,EAAqBzB,IAD3E,QAoIF,OAAOX,IACND,SAAUE"} -------------------------------------------------------------------------------- /spec/javascripts/selectable.spec.js: -------------------------------------------------------------------------------- 1 | describe("selectable model", function(){ 2 | var Model = Backbone.Model.extend({ 3 | initialize: function(){ 4 | Backbone.Picky.Selectable.applyTo(this); 5 | } 6 | }); 7 | 8 | describe("when selecting a model", function(){ 9 | var model; 10 | 11 | beforeEach(function(){ 12 | model = new Model(); 13 | spyOn(model, "trigger").andCallThrough(); 14 | 15 | model.select(); 16 | }); 17 | 18 | it("should be selected", function(){ 19 | expect(model.selected).toBe(true); 20 | }); 21 | 22 | it("should notify of selection", function(){ 23 | expect(model.trigger).toHaveBeenCalledWithInitial("selected", model); 24 | }); 25 | 26 | it("should not trigger a reselected event", function(){ 27 | expect(model.trigger).not.toHaveBeenCalledWithInitial("reselected"); 28 | }); 29 | }); 30 | 31 | describe("when selecting a model, with options.silent enabled", function(){ 32 | var model; 33 | 34 | beforeEach(function(){ 35 | model = new Model(); 36 | spyOn(model, "trigger").andCallThrough(); 37 | 38 | model.select({silent: true}); 39 | }); 40 | 41 | it("should be selected", function(){ 42 | expect(model.selected).toBe(true); 43 | }); 44 | 45 | it("should not notify of selection", function(){ 46 | expect(model.trigger).not.toHaveBeenCalledWithInitial("selected"); 47 | }); 48 | }); 49 | 50 | describe("when selecting a model that is already selected", function(){ 51 | var model; 52 | 53 | beforeEach(function(){ 54 | model = new Model(); 55 | model.select(); 56 | 57 | spyOn(model, "trigger").andCallThrough(); 58 | model.select(); 59 | }); 60 | 61 | it("should still be selected", function(){ 62 | expect(model.selected).toBe(true); 63 | }); 64 | 65 | it("should not notify of selection", function(){ 66 | expect(model.trigger).not.toHaveBeenCalledWithInitial("selected"); 67 | }); 68 | 69 | it("should trigger a reselected event", function(){ 70 | expect(model.trigger).toHaveBeenCalledWithInitial("reselected", model); 71 | }); 72 | }); 73 | 74 | describe("when selecting a model that is already selected, with options.silent enabled", function(){ 75 | var model; 76 | 77 | beforeEach(function(){ 78 | model = new Model(); 79 | model.select(); 80 | 81 | spyOn(model, "trigger").andCallThrough(); 82 | model.select({silent: true}); 83 | }); 84 | 85 | it("should still be selected", function(){ 86 | expect(model.selected).toBe(true); 87 | }); 88 | 89 | it("should not notify of selection", function(){ 90 | expect(model.trigger).not.toHaveBeenCalledWithInitial("selected"); 91 | }); 92 | 93 | it("should not trigger a reselected event", function(){ 94 | expect(model.trigger).not.toHaveBeenCalledWithInitial("reselected"); 95 | }); 96 | }); 97 | 98 | describe("when deselecting a model that has been selected", function(){ 99 | var model; 100 | 101 | beforeEach(function(){ 102 | model = new Model(); 103 | model.select(); 104 | 105 | spyOn(model, "trigger").andCallThrough(); 106 | model.deselect(); 107 | }); 108 | 109 | it("should not be selected", function(){ 110 | expect(model.selected).toBe(false); 111 | }); 112 | 113 | it("should notify of deselection", function(){ 114 | expect(model.trigger).toHaveBeenCalledWithInitial("deselected", model); 115 | }); 116 | }); 117 | 118 | describe("when deselecting a model that has been selected, with options.silent enabled", function(){ 119 | var model; 120 | 121 | beforeEach(function(){ 122 | model = new Model(); 123 | model.select(); 124 | 125 | spyOn(model, "trigger").andCallThrough(); 126 | model.deselect({silent: true}); 127 | }); 128 | 129 | it("should not be selected", function(){ 130 | expect(model.selected).toBe(false); 131 | }); 132 | 133 | it("should not notify of deselection", function(){ 134 | expect(model.trigger).not.toHaveBeenCalledWithInitial("deselected"); 135 | }); 136 | }); 137 | 138 | describe("when deselecting a model that is not selected", function(){ 139 | var model; 140 | 141 | beforeEach(function(){ 142 | model = new Model(); 143 | 144 | spyOn(model, "trigger").andCallThrough(); 145 | model.deselect(); 146 | }); 147 | 148 | it("should not be selected", function(){ 149 | expect(model.selected).toBeFalsy(); 150 | }); 151 | 152 | it("should not notify of deselection", function(){ 153 | expect(model.trigger).not.toHaveBeenCalledWithInitial("deselected"); 154 | }); 155 | 156 | it("should not trigger a reselected event", function(){ 157 | expect(model.trigger).not.toHaveBeenCalledWithInitial("reselected"); 158 | }); 159 | }); 160 | 161 | describe("when toggling the selected status of a model that is selected", function(){ 162 | var model; 163 | 164 | beforeEach(function(){ 165 | model = new Model(); 166 | model.select(); 167 | 168 | spyOn(model, "trigger").andCallThrough(); 169 | model.toggleSelected(); 170 | }); 171 | 172 | it("should not be selected", function(){ 173 | expect(model.selected).toBe(false); 174 | }); 175 | 176 | it("should notify of deselection", function(){ 177 | expect(model.trigger).toHaveBeenCalledWithInitial("deselected", model); 178 | }); 179 | }); 180 | 181 | describe("when toggling the selected status of a model that is not selected", function(){ 182 | var model; 183 | 184 | beforeEach(function(){ 185 | model = new Model(); 186 | 187 | spyOn(model, "trigger").andCallThrough(); 188 | model.toggleSelected(); 189 | }); 190 | 191 | it("should be selected", function(){ 192 | expect(model.selected).toBe(true); 193 | }); 194 | 195 | it("should notify of selection", function(){ 196 | expect(model.trigger).toHaveBeenCalledWithInitial("selected", model); 197 | }); 198 | }); 199 | 200 | describe('custom options', function () { 201 | 202 | describe("when selecting a model with a custom option", function () { 203 | var model; 204 | 205 | beforeEach(function () { 206 | model = new Model(); 207 | spyOn(model, "trigger").andCallThrough(); 208 | 209 | model.select({foo: "bar"}); 210 | }); 211 | 212 | it("should trigger a selected event and pass the the options object along as the last parameter", function () { 213 | expect(model.trigger).toHaveBeenCalledWith("selected", model, {foo: "bar"}); 214 | }); 215 | }); 216 | 217 | describe("when re-selecting a model with a custom option", function () { 218 | var model; 219 | 220 | beforeEach(function () { 221 | model = new Model(); 222 | model.select(); 223 | 224 | spyOn(model, "trigger").andCallThrough(); 225 | model.select({foo: "bar"}); 226 | }); 227 | 228 | it("should trigger a reselected event and pass the the options object along as the last parameter", function () { 229 | expect(model.trigger).toHaveBeenCalledWith("reselected", model, {foo: "bar"}); 230 | }); 231 | }); 232 | 233 | describe("when deselecting a model with a custom option", function () { 234 | var model; 235 | 236 | beforeEach(function () { 237 | model = new Model(); 238 | model.select(); 239 | 240 | spyOn(model, "trigger").andCallThrough(); 241 | model.deselect({foo: "bar"}); 242 | }); 243 | 244 | it("should trigger a deselected event and pass the the options object along as the last parameter", function () { 245 | expect(model.trigger).toHaveBeenCalledWith("deselected", model, {foo: "bar"}); 246 | }); 247 | }); 248 | 249 | describe("when toggling the selected status of a model that is selected, with a custom option", function () { 250 | var model; 251 | 252 | beforeEach(function () { 253 | model = new Model(); 254 | model.select(); 255 | 256 | spyOn(model, "trigger").andCallThrough(); 257 | model.toggleSelected({foo: "bar"}); 258 | }); 259 | 260 | it("should trigger a deselected event and pass the the options object along as the last parameter", function () { 261 | expect(model.trigger).toHaveBeenCalledWith("deselected", model, {foo: "bar"}); 262 | }); 263 | }); 264 | 265 | describe("when toggling the selected status of a model that is not selected, with a custom option", function () { 266 | var model; 267 | 268 | beforeEach(function () { 269 | model = new Model(); 270 | 271 | spyOn(model, "trigger").andCallThrough(); 272 | model.toggleSelected({foo: "bar"}); 273 | }); 274 | 275 | it("should trigger a selected event and pass the the options object along as the last parameter", function () { 276 | expect(model.trigger).toHaveBeenCalledWith("selected", model, {foo: "bar"}); 277 | }); 278 | }); 279 | 280 | }); 281 | 282 | describe('automatic invocation of onSelect, onDeselect, onReselect handlers', function () { 283 | var EventHandlingModel, model; 284 | 285 | beforeEach(function () { 286 | 287 | EventHandlingModel = Model.extend({ 288 | onSelect: function () {}, 289 | onDeselect: function () {}, 290 | onReselect: function () {} 291 | }); 292 | 293 | model = new EventHandlingModel(); 294 | 295 | spyOn(model, "onSelect").andCallThrough(); 296 | spyOn(model, "onDeselect").andCallThrough(); 297 | spyOn(model, "onReselect").andCallThrough(); 298 | }); 299 | 300 | it('calls the onSelect handler when triggering a selected event on the model', function () { 301 | model.trigger("selected", model, {foo: "bar"}); 302 | expect(model.onSelect).toHaveBeenCalledWith(model, {foo: "bar"}); 303 | }); 304 | 305 | it('calls the onDeselect handler when triggering a deselected event on the model', function () { 306 | model.trigger("deselected", model, {foo: "bar"}); 307 | expect(model.onDeselect).toHaveBeenCalledWith(model, {foo: "bar"}); 308 | }); 309 | 310 | it('calls the onReselect handler when triggering a reselected event on the model', function () { 311 | model.trigger("reselected", model, {foo: "bar"}); 312 | expect(model.onReselect).toHaveBeenCalledWith(model, {foo: "bar"}); 313 | }); 314 | }); 315 | 316 | }); 317 | -------------------------------------------------------------------------------- /spec/javascripts/multiSelect.deselectAll.spec.js: -------------------------------------------------------------------------------- 1 | describe("multi-select collection: deselectAll", function(){ 2 | var Model = Backbone.Model.extend({ 3 | initialize: function(){ 4 | Backbone.Picky.Selectable.applyTo(this); 5 | } 6 | }); 7 | 8 | var Collection = Backbone.Collection.extend({ 9 | model: Model, 10 | 11 | initialize: function(){ 12 | Backbone.Picky.MultiSelect.applyTo(this); 13 | } 14 | }); 15 | 16 | describe("when no models are selected, and deselecting all", function(){ 17 | var m1, m2, collection; 18 | 19 | beforeEach(function(){ 20 | m1 = new Model(); 21 | m2 = new Model(); 22 | 23 | collection = new Collection([m1, m2]); 24 | spyOn(collection, "trigger").andCallThrough(); 25 | 26 | collection.deselectAll(); 27 | }); 28 | 29 | it("should not trigger a select:none event", function(){ 30 | // NB This is a change in the spec. Up to version 0.2.0, it _did_ trigger 31 | // a select:none event. But an event triggered by a no-op didn't make 32 | // sense and was inconsistent with the behaviour elsewhere. 33 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("select:none"); 34 | }); 35 | 36 | it("should have a selected count of 0", function(){ 37 | expect(collection.selectedLength).toBe(0); 38 | }); 39 | 40 | it("should not have any models in the selected list", function(){ 41 | var size = _.size(collection.selected); 42 | expect(size).toBe(0); 43 | }); 44 | }); 45 | 46 | describe("when no models are selected, and deselecting all, with options.silent enabled", function(){ 47 | var m1, m2, collection; 48 | 49 | beforeEach(function(){ 50 | m1 = new Model(); 51 | m2 = new Model(); 52 | 53 | collection = new Collection([m1, m2]); 54 | spyOn(collection, "trigger").andCallThrough(); 55 | 56 | collection.deselectAll({silent: true}); 57 | }); 58 | 59 | it("should not trigger a select:none event", function(){ 60 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("select:none"); 61 | }); 62 | 63 | it("should have a selected count of 0", function(){ 64 | expect(collection.selectedLength).toBe(0); 65 | }); 66 | 67 | it("should not have any models in the selected list", function(){ 68 | var size = _.size(collection.selected); 69 | expect(size).toBe(0); 70 | }); 71 | }); 72 | 73 | describe("when 1 model is selected, and deselecting all", function(){ 74 | var m1, m2, collection; 75 | 76 | beforeEach(function(){ 77 | m1 = new Model(); 78 | m2 = new Model(); 79 | 80 | collection = new Collection([m1, m2]); 81 | m1.select(); 82 | 83 | spyOn(collection, "trigger").andCallThrough(); 84 | collection.deselectAll(); 85 | }); 86 | 87 | it("should trigger a select:none event", function(){ 88 | expect(collection.trigger).toHaveBeenCalledWithInitial("select:none", { selected: [], deselected: [m1] }, collection); 89 | }); 90 | 91 | it("should not trigger a select:some event", function(){ 92 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("select:some"); 93 | }); 94 | 95 | it("should not trigger a reselect:any event", function(){ 96 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("reselect:any"); 97 | }); 98 | 99 | it("should have a selected count of 0", function(){ 100 | expect(collection.selectedLength).toBe(0); 101 | }); 102 | 103 | it("should not have any models in the selected list", function(){ 104 | var size = _.size(collection.selected); 105 | expect(size).toBe(0); 106 | }); 107 | }); 108 | 109 | describe("when 1 model is selected, and deselecting all, with options.silent enabled", function(){ 110 | var m1, m2, collection; 111 | 112 | beforeEach(function(){ 113 | m1 = new Model(); 114 | m2 = new Model(); 115 | 116 | collection = new Collection([m1, m2]); 117 | m1.select(); 118 | 119 | spyOn(collection, "trigger").andCallThrough(); 120 | collection.deselectAll({silent: true}); 121 | }); 122 | 123 | it("should not trigger a select:none event", function(){ 124 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("select:none"); 125 | }); 126 | 127 | it("should have a selected count of 0", function(){ 128 | expect(collection.selectedLength).toBe(0); 129 | }); 130 | 131 | it("should not have any models in the selected list", function(){ 132 | var size = _.size(collection.selected); 133 | expect(size).toBe(0); 134 | }); 135 | }); 136 | 137 | describe("when 1 model is selected, and deselecting all (selectNone)", function(){ 138 | var m1, m2, collection; 139 | 140 | beforeEach(function(){ 141 | m1 = new Model(); 142 | m2 = new Model(); 143 | 144 | collection = new Collection([m1, m2]); 145 | m1.select(); 146 | 147 | spyOn(collection, "trigger").andCallThrough(); 148 | collection.selectNone(); 149 | }); 150 | 151 | it("should trigger a select:none event", function(){ 152 | expect(collection.trigger).toHaveBeenCalledWithInitial("select:none", { selected: [], deselected: [m1] }, collection); 153 | }); 154 | 155 | it("should not trigger a reselect:any event", function(){ 156 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("reselect:any"); 157 | }); 158 | 159 | it("should have a selected count of 0", function(){ 160 | expect(collection.selectedLength).toBe(0); 161 | }); 162 | 163 | it("should not have any models in the selected list", function(){ 164 | var size = _.size(collection.selected); 165 | expect(size).toBe(0); 166 | }); 167 | }); 168 | 169 | describe("when all models are selected, and deselecting all", function(){ 170 | var m1, m2, collection, selectedEventState, selectNoneEventState; 171 | 172 | beforeEach(function(){ 173 | selectedEventState = { model: {}, collection: {} }; 174 | selectNoneEventState = { m1: {}, m2: {}, collection: {} }; 175 | 176 | m1 = new Model(); 177 | m2 = new Model(); 178 | 179 | collection = new Collection([m1, m2]); 180 | m1.select(); 181 | m2.select(); 182 | 183 | spyOn(collection, "trigger").andCallThrough(); 184 | 185 | m1.on('deselected', function (model) { 186 | selectedEventState.model.selected = model && model.selected; 187 | selectedEventState.collection.selected = _.clone(collection.selected); 188 | selectedEventState.collection.selectedLength = collection.selectedLength; 189 | }); 190 | 191 | collection.on('select:none', function () { 192 | selectNoneEventState.m1.selected = m1.selected; 193 | selectNoneEventState.m2.selected = m2.selected; 194 | selectNoneEventState.collection.selected = _.clone(collection.selected); 195 | selectNoneEventState.collection.selectedLength = collection.selectedLength; 196 | }); 197 | 198 | collection.deselectAll(); 199 | }); 200 | 201 | it("should trigger a select:none event", function(){ 202 | expect(collection.trigger).toHaveBeenCalledWithInitial("select:none", { selected: [], deselected: [m1, m2] }, collection); 203 | }); 204 | 205 | it("should not trigger a select:some event", function(){ 206 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("select:some"); 207 | }); 208 | 209 | it("should not trigger a reselect:any event", function(){ 210 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("reselect:any"); 211 | }); 212 | 213 | it("should have a selected count of 0", function(){ 214 | expect(collection.selectedLength).toBe(0); 215 | }); 216 | 217 | it("should not have any models in the selected list", function(){ 218 | var size = _.size(collection.selected); 219 | expect(size).toBe(0); 220 | }); 221 | 222 | it('should trigger a model\'s deselected event after the model status has been updated', function () { 223 | expect(selectedEventState.model.selected).toEqual(false); 224 | }); 225 | 226 | it('should trigger a model\'s selected event after the collection\'s selected models have been updated with that model', function () { 227 | // m2 doesn't necessarily have to be removed from collection.selected at 228 | // this time. The point is that events are fired when model and collection 229 | // states are consistent. When m1 fires the 'deselected' event, only m1 230 | // must have been removed from the collection. 231 | expect(selectedEventState.collection.selected[m1.cid]).toBeUndefined(); 232 | }); 233 | 234 | it('should trigger a model\'s selected event after the collection\'s selected length has been updated', function () { 235 | // collection.selectedLength could be 0 or 1 at this time. Again, all we 236 | // are asking for is consistency - see comment above. 237 | expect(selectedEventState.collection.selectedLength).toBeLessThan(2); 238 | expect(selectedEventState.collection.selectedLength).toEqual(_.size(selectedEventState.collection.selected)); 239 | }); 240 | 241 | it('should trigger the collection\'s select:none event after the model status has been updated', function () { 242 | expect(selectNoneEventState.m1.selected).toEqual(false); 243 | expect(selectNoneEventState.m2.selected).toEqual(false); 244 | }); 245 | 246 | it('should trigger the collection\'s select:none event after the collection\'s selected models have been updated', function () { 247 | expect(selectNoneEventState.collection.selected).toEqual({}); 248 | }); 249 | 250 | it('should trigger the collection\'s select:none event after the collection\'s selected length has been updated', function () { 251 | expect(selectNoneEventState.collection.selectedLength).toBe(0); 252 | }); 253 | }); 254 | 255 | describe("when all models are selected, and deselecting all (selectNone)", function(){ 256 | var m1, m2, collection; 257 | 258 | beforeEach(function(){ 259 | m1 = new Model(); 260 | m2 = new Model(); 261 | 262 | collection = new Collection([m1, m2]); 263 | m1.select(); 264 | m2.select(); 265 | 266 | spyOn(collection, "trigger").andCallThrough(); 267 | collection.selectNone(); 268 | }); 269 | 270 | it("should trigger a select:none event", function(){ 271 | expect(collection.trigger).toHaveBeenCalledWithInitial("select:none", { selected: [], deselected: [m1, m2] }, collection); 272 | }); 273 | 274 | it("should not trigger a reselect:any event", function(){ 275 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("reselect:any"); 276 | }); 277 | 278 | it("should have a selected count of 0", function(){ 279 | expect(collection.selectedLength).toBe(0); 280 | }); 281 | 282 | it("should not have any models in the selected list", function(){ 283 | var size = _.size(collection.selected); 284 | expect(size).toBe(0); 285 | }); 286 | }); 287 | 288 | describe("when all models are selected, and deselecting all, with options.silent enabled", function(){ 289 | var m1, m2, collection; 290 | 291 | beforeEach(function(){ 292 | m1 = new Model(); 293 | m2 = new Model(); 294 | 295 | collection = new Collection([m1, m2]); 296 | m1.select(); 297 | m2.select(); 298 | 299 | spyOn(collection, "trigger").andCallThrough(); 300 | collection.deselectAll({silent: true}); 301 | }); 302 | 303 | it("should not trigger a select:none event", function(){ 304 | expect(collection.trigger).not.toHaveBeenCalledWithInitial("select:none"); 305 | }); 306 | 307 | it("should have a selected count of 0", function(){ 308 | expect(collection.selectedLength).toBe(0); 309 | }); 310 | 311 | it("should not have any models in the selected list", function(){ 312 | var size = _.size(collection.selected); 313 | expect(size).toBe(0); 314 | }); 315 | }); 316 | 317 | describe("when all models are selected, and deselecting all with a custom option", function(){ 318 | var m1, m2, collection; 319 | 320 | beforeEach(function(){ 321 | m1 = new Model(); 322 | m2 = new Model(); 323 | 324 | collection = new Collection([m1, m2]); 325 | m1.select(); 326 | m2.select(); 327 | 328 | spyOn(m1, "trigger").andCallThrough(); 329 | spyOn(m2, "trigger").andCallThrough(); 330 | spyOn(collection, "trigger").andCallThrough(); 331 | 332 | collection.deselectAll({foo: "bar"}); 333 | }); 334 | 335 | it("should trigger a deselected event on the first model and pass the options object along as the last parameter", function(){ 336 | expect(m1.trigger).toHaveBeenCalledWith("deselected", m1, {foo: "bar"}); 337 | }); 338 | 339 | it("should trigger a deselected event on the second model and pass the options object along as the last parameter", function(){ 340 | expect(m2.trigger).toHaveBeenCalledWith("deselected", m2, {foo: "bar"}); 341 | }); 342 | 343 | it("should trigger a select:none event and pass the options object along as the last parameter", function(){ 344 | expect(collection.trigger).toHaveBeenCalledWith("select:none", { selected: [], deselected: [m1, m2] }, collection, {foo: "bar"}); 345 | }); 346 | }); 347 | 348 | }); 349 | -------------------------------------------------------------------------------- /reports/assets/scripts/vendor/bootstrap-tooltip.js: -------------------------------------------------------------------------------- 1 | /* ======================================================================== 2 | * Bootstrap: tooltip.js v3.0.0 3 | * http://twbs.github.com/bootstrap/javascript.html#tooltip 4 | * Inspired by the original jQuery.tipsy by Jason Frame 5 | * ======================================================================== 6 | * Copyright 2012 Twitter, Inc. 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * ======================================================================== */ 20 | 21 | 22 | +function ($) { "use strict"; 23 | 24 | // TOOLTIP PUBLIC CLASS DEFINITION 25 | // =============================== 26 | 27 | var Tooltip = function (element, options) { 28 | this.type = 29 | this.options = 30 | this.enabled = 31 | this.timeout = 32 | this.hoverState = 33 | this.$element = null 34 | 35 | this.init('tooltip', element, options) 36 | } 37 | 38 | Tooltip.DEFAULTS = { 39 | animation: true 40 | , placement: 'top' 41 | , selector: false 42 | , template: '
      ' 43 | , trigger: 'hover focus' 44 | , title: '' 45 | , delay: 0 46 | , html: false 47 | , container: false 48 | } 49 | 50 | Tooltip.prototype.init = function (type, element, options) { 51 | this.enabled = true 52 | this.type = type 53 | this.$element = $(element) 54 | this.options = this.getOptions(options) 55 | 56 | var triggers = this.options.trigger.split(' ') 57 | 58 | for (var i = triggers.length; i--;) { 59 | var trigger = triggers[i] 60 | 61 | if (trigger == 'click') { 62 | this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) 63 | } else if (trigger != 'manual') { 64 | var eventIn = trigger == 'hover' ? 'mouseenter' : 'focus' 65 | var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur' 66 | 67 | this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) 68 | this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) 69 | } 70 | } 71 | 72 | this.options.selector ? 73 | (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : 74 | this.fixTitle() 75 | } 76 | 77 | Tooltip.prototype.getDefaults = function () { 78 | return Tooltip.DEFAULTS 79 | } 80 | 81 | Tooltip.prototype.getOptions = function (options) { 82 | options = $.extend({}, this.getDefaults(), this.$element.data(), options) 83 | 84 | if (options.delay && typeof options.delay == 'number') { 85 | options.delay = { 86 | show: options.delay 87 | , hide: options.delay 88 | } 89 | } 90 | 91 | return options 92 | } 93 | 94 | Tooltip.prototype.getDelegateOptions = function () { 95 | var options = {} 96 | var defaults = this.getDefaults() 97 | 98 | this._options && $.each(this._options, function (key, value) { 99 | if (defaults[key] != value) options[key] = value 100 | }) 101 | 102 | return options 103 | } 104 | 105 | Tooltip.prototype.enter = function (obj) { 106 | var self = obj instanceof this.constructor ? 107 | obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) 108 | 109 | clearTimeout(self.timeout) 110 | 111 | self.hoverState = 'in' 112 | 113 | if (!self.options.delay || !self.options.delay.show) return self.show() 114 | 115 | self.timeout = setTimeout(function () { 116 | if (self.hoverState == 'in') self.show() 117 | }, self.options.delay.show) 118 | } 119 | 120 | Tooltip.prototype.leave = function (obj) { 121 | var self = obj instanceof this.constructor ? 122 | obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) 123 | 124 | clearTimeout(self.timeout) 125 | 126 | self.hoverState = 'out' 127 | 128 | if (!self.options.delay || !self.options.delay.hide) return self.hide() 129 | 130 | self.timeout = setTimeout(function () { 131 | if (self.hoverState == 'out') self.hide() 132 | }, self.options.delay.hide) 133 | } 134 | 135 | Tooltip.prototype.show = function () { 136 | var e = $.Event('show.bs.'+ this.type) 137 | 138 | if (this.hasContent() && this.enabled) { 139 | this.$element.trigger(e) 140 | 141 | if (e.isDefaultPrevented()) return 142 | 143 | var $tip = this.tip() 144 | 145 | this.setContent() 146 | 147 | if (this.options.animation) $tip.addClass('fade') 148 | 149 | var placement = typeof this.options.placement == 'function' ? 150 | this.options.placement.call(this, $tip[0], this.$element[0]) : 151 | this.options.placement 152 | 153 | var autoToken = /\s?auto?\s?/i 154 | var autoPlace = autoToken.test(placement) 155 | if (autoPlace) placement = placement.replace(autoToken, '') || 'top' 156 | 157 | $tip 158 | .detach() 159 | .css({ top: 0, left: 0, display: 'block' }) 160 | .addClass(placement) 161 | 162 | this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) 163 | 164 | var pos = this.getPosition() 165 | var actualWidth = $tip[0].offsetWidth 166 | var actualHeight = $tip[0].offsetHeight 167 | 168 | if (autoPlace) { 169 | var $parent = this.$element.parent() 170 | 171 | var orgPlacement = placement 172 | var docScroll = document.documentElement.scrollTop || document.body.scrollTop 173 | var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth() 174 | var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight() 175 | var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left 176 | 177 | placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' : 178 | placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' : 179 | placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' : 180 | placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' : 181 | placement 182 | 183 | $tip 184 | .removeClass(orgPlacement) 185 | .addClass(placement) 186 | } 187 | 188 | var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) 189 | 190 | this.applyPlacement(calculatedOffset, placement) 191 | this.$element.trigger('shown.bs.' + this.type) 192 | } 193 | } 194 | 195 | Tooltip.prototype.applyPlacement = function(offset, placement) { 196 | var replace 197 | var $tip = this.tip() 198 | var width = $tip[0].offsetWidth 199 | var height = $tip[0].offsetHeight 200 | 201 | // manually read margins because getBoundingClientRect includes difference 202 | var marginTop = parseInt($tip.css('margin-top'), 10) 203 | var marginLeft = parseInt($tip.css('margin-left'), 10) 204 | 205 | // we must check for NaN for ie 8/9 206 | if (isNaN(marginTop)) marginTop = 0 207 | if (isNaN(marginLeft)) marginLeft = 0 208 | 209 | offset.top = offset.top + marginTop 210 | offset.left = offset.left + marginLeft 211 | 212 | $tip 213 | .offset(offset) 214 | .addClass('in') 215 | 216 | // check to see if placing tip in new offset caused the tip to resize itself 217 | var actualWidth = $tip[0].offsetWidth 218 | var actualHeight = $tip[0].offsetHeight 219 | 220 | if (placement == 'top' && actualHeight != height) { 221 | replace = true 222 | offset.top = offset.top + height - actualHeight 223 | } 224 | 225 | if (/bottom|top/.test(placement)) { 226 | var delta = 0 227 | 228 | if (offset.left < 0) { 229 | delta = offset.left * -2 230 | offset.left = 0 231 | 232 | $tip.offset(offset) 233 | 234 | actualWidth = $tip[0].offsetWidth 235 | actualHeight = $tip[0].offsetHeight 236 | } 237 | 238 | this.replaceArrow(delta - width + actualWidth, actualWidth, 'left') 239 | } else { 240 | this.replaceArrow(actualHeight - height, actualHeight, 'top') 241 | } 242 | 243 | if (replace) $tip.offset(offset) 244 | } 245 | 246 | Tooltip.prototype.replaceArrow = function(delta, dimension, position) { 247 | this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + "%") : '') 248 | } 249 | 250 | Tooltip.prototype.setContent = function () { 251 | var $tip = this.tip() 252 | var title = this.getTitle() 253 | 254 | $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) 255 | $tip.removeClass('fade in top bottom left right') 256 | } 257 | 258 | Tooltip.prototype.hide = function () { 259 | var that = this 260 | var $tip = this.tip() 261 | var e = $.Event('hide.bs.' + this.type) 262 | 263 | function complete() { 264 | if (that.hoverState != 'in') $tip.detach() 265 | } 266 | 267 | this.$element.trigger(e) 268 | 269 | if (e.isDefaultPrevented()) return 270 | 271 | $tip.removeClass('in') 272 | 273 | $.support.transition && this.$tip.hasClass('fade') ? 274 | $tip 275 | .one($.support.transition.end, complete) 276 | .emulateTransitionEnd(150) : 277 | complete() 278 | 279 | this.$element.trigger('hidden.bs.' + this.type) 280 | 281 | return this 282 | } 283 | 284 | Tooltip.prototype.fixTitle = function () { 285 | var $e = this.$element 286 | if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') { 287 | $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') 288 | } 289 | } 290 | 291 | Tooltip.prototype.hasContent = function () { 292 | return this.getTitle() 293 | } 294 | 295 | Tooltip.prototype.getPosition = function () { 296 | var el = this.$element[0] 297 | return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : { 298 | width: el.offsetWidth 299 | , height: el.offsetHeight 300 | }, this.$element.offset()) 301 | } 302 | 303 | Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { 304 | return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : 305 | placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : 306 | placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : 307 | /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } 308 | } 309 | 310 | Tooltip.prototype.getTitle = function () { 311 | var title 312 | var $e = this.$element 313 | var o = this.options 314 | 315 | title = $e.attr('data-original-title') 316 | || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) 317 | 318 | return title 319 | } 320 | 321 | Tooltip.prototype.tip = function () { 322 | return this.$tip = this.$tip || $(this.options.template) 323 | } 324 | 325 | Tooltip.prototype.arrow = function () { 326 | return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow') 327 | } 328 | 329 | Tooltip.prototype.validate = function () { 330 | if (!this.$element[0].parentNode) { 331 | this.hide() 332 | this.$element = null 333 | this.options = null 334 | } 335 | } 336 | 337 | Tooltip.prototype.enable = function () { 338 | this.enabled = true 339 | } 340 | 341 | Tooltip.prototype.disable = function () { 342 | this.enabled = false 343 | } 344 | 345 | Tooltip.prototype.toggleEnabled = function () { 346 | this.enabled = !this.enabled 347 | } 348 | 349 | Tooltip.prototype.toggle = function (e) { 350 | var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this 351 | self.tip().hasClass('in') ? self.leave(self) : self.enter(self) 352 | } 353 | 354 | Tooltip.prototype.destroy = function () { 355 | this.hide().$element.off('.' + this.type).removeData('bs.' + this.type) 356 | } 357 | 358 | 359 | // TOOLTIP PLUGIN DEFINITION 360 | // ========================= 361 | 362 | var old = $.fn.tooltip 363 | 364 | $.fn.tooltip = function (option) { 365 | return this.each(function () { 366 | var $this = $(this) 367 | var data = $this.data('bs.tooltip') 368 | var options = typeof option == 'object' && option 369 | 370 | if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) 371 | if (typeof option == 'string') data[option]() 372 | }) 373 | } 374 | 375 | $.fn.tooltip.Constructor = Tooltip 376 | 377 | 378 | // TOOLTIP NO CONFLICT 379 | // =================== 380 | 381 | $.fn.tooltip.noConflict = function () { 382 | $.fn.tooltip = old 383 | return this 384 | } 385 | 386 | }(window.jQuery); 387 | --------------------------------------------------------------------------------