├── .gitignore ├── LICENSE ├── Nakefile ├── README.md ├── dist ├── casting │ ├── header.js │ └── layout.js ├── dnd │ ├── header.js │ └── layout.js ├── effects │ ├── header.js │ ├── layout.js │ └── lint.js ├── jquerysh │ ├── header.js │ ├── layout.js │ └── lint.js ├── json │ ├── header.js │ ├── layout.js │ └── lint.js ├── keys │ ├── header.js │ └── layout.js ├── rails │ ├── header.js │ └── layout.js ├── sizzle │ ├── header.js │ ├── layout.js │ └── lint.js └── table │ ├── header.js │ └── layout.js ├── src ├── casting │ ├── __init__.js │ └── casting.js ├── dnd │ ├── __init__.js │ ├── document.js │ ├── draggable.js │ ├── droppable.js │ └── element.js ├── effects │ ├── __init__.js │ ├── element.js │ └── fx │ │ ├── bounce.js │ │ ├── glow.js │ │ ├── move.js │ │ ├── puff.js │ │ ├── run.js │ │ └── zoom.js ├── jquerysh │ ├── __init__.js │ ├── ajax.js │ ├── dollar.js │ ├── element.js │ ├── jquery.js │ └── jquerysh.js ├── json │ ├── __init__.js │ ├── cookie.js │ └── json.js ├── keys │ ├── __init__.js │ ├── element.js │ └── event.js ├── lang │ ├── hash.js │ ├── header.js │ ├── num_range.js │ ├── shortcuts.js │ └── string.js ├── rails │ ├── __init__.js │ ├── aliases.js │ ├── document.js │ ├── rr.js │ └── ujs.js ├── sizzle │ ├── __init__.js │ ├── hooks.js │ └── sizzle.js └── table │ ├── __init__.js │ ├── document.js │ └── table.js ├── test ├── casting.html ├── dnd.html ├── effects.html ├── index.html ├── jquerysh.html ├── json │ └── json_test.js ├── keys.html ├── lang │ └── string_test.js ├── rails │ └── rails_test.js ├── sizzle.html └── table.html └── util ├── jslint.js ├── linter.js ├── nake.js ├── right-safe.js ├── right-server.js ├── right.js ├── source.js ├── test-page.css ├── testcase.js ├── tools.js └── ugly ├── parse-js.js ├── process.js └── squeeze-more.js /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2010 Nikolay V. Nemshilov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Nakefile: -------------------------------------------------------------------------------- 1 | /** 2 | * The 'nake' util taks 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | 7 | var // the build constants 8 | BUILD_DIRECTORY = 'build', 9 | BUILD_PREFIX = 'right', 10 | BUILD_EXCLUDE = ['jqvm', 'lang']; // the don't build list 11 | 12 | var // importint the Nake tools 13 | on_npm = process.argv[1].match(/nake$/), 14 | Nake = require(on_npm ? 'nake' : './util/nake'), 15 | task = Nake.task, 16 | namespace = Nake.namespace; 17 | 18 | Nake.Default = 'build'; // making the build by default 19 | 20 | 21 | var // sources processing tools 22 | Source = require('./util/source').Source, 23 | builds = {}; // global build reference 24 | 25 | 26 | //////////////////////////////////////////////////////////////// 27 | // Parsing the options 28 | //////////////////////////////////////////////////////////////// 29 | var fs = require('fs'); 30 | var options = Nake.ARGS.OPTIONS ? Nake.ARGS.OPTIONS.split(',') : []; 31 | var widgets = fs.readdirSync('dist').filter(function(filename) { 32 | return filename.match(/^[a-z_]+$/) && options.empty() ? 33 | !BUILD_EXCLUDE.includes(filename) : 34 | options.include(filename); 35 | }); 36 | 37 | 38 | //////////////////////////////////////////////////////////////// 39 | // Cleaning up the build directory 40 | //////////////////////////////////////////////////////////////// 41 | task('clean', 'Clean up the build directory', function() { 42 | try { 43 | this.step("Deleting an old directory"); 44 | 45 | fs.readdirSync(BUILD_DIRECTORY).each(function(file) { 46 | fs.unlinkSync(BUILD_DIRECTORY + '/' + file); 47 | }); 48 | fs.rmdirSync(BUILD_DIRECTORY); 49 | } catch(e) {} 50 | 51 | this.step("Creating the build directory"); 52 | fs.mkdirSync(BUILD_DIRECTORY, 0755); 53 | }); 54 | 55 | 56 | //////////////////////////////////////////////////////////////// 57 | // Paking the widgets into single source files 58 | //////////////////////////////////////////////////////////////// 59 | task('pack', 'Pack up the source code', function() { 60 | Nake.run('clean', 'Cleaning up the build directory'); 61 | 62 | this.step('Packing up the widgets'); 63 | widgets.each(function(name) { 64 | this.step('\b\b - '+ name.capitalize()); 65 | 66 | var init = fs.readFileSync('src/'+ name + '/__init__.js')+''; 67 | var files = ['src/' + name + '/__init__.js']; 68 | 69 | 70 | // parsing out the widget's own files 71 | init.replace(/include_module_files\(([^\)]+)\)(;*)/mg, function(m, content) { 72 | content.replace(/('|")([\w\d\_\-\/]+)\1/g, function(m, q, filename) { 73 | files.push("src/"+ name + "/"+ filename + ".js"); 74 | }); 75 | }); 76 | 77 | var source = new Source({ 78 | files: files, 79 | header: 'dist/'+ name +'/header.js', 80 | layout: 'dist/'+ name +'/layout.js' 81 | }); 82 | 83 | source.patch(function(source) { 84 | return source 85 | .replace(/include_shared_js\(([^\)]+)\)(;*)/mg, '') 86 | .replace(/include_shared_css\(([^\)]+)\)(;*)/mg, '') 87 | .replace(/include_module_files\([^\)]+\)(;*)/mg, ''); 88 | }); 89 | 90 | source.write(BUILD_DIRECTORY + '/' + BUILD_PREFIX + "-" + name.replace('_', '-')); 91 | 92 | builds[name] = source; 93 | }, this); 94 | }); 95 | 96 | 97 | //////////////////////////////////////////////////////////////// 98 | // Checking the widgets with JSLint 99 | //////////////////////////////////////////////////////////////// 100 | task('check', 'Check the source builds with JSLint', function() { 101 | Nake.run('pack', 'Packing the widget source files'); 102 | 103 | this.step('Running JSLint on widgets') 104 | for (var name in builds) { 105 | var linter = builds[name].linter('dist/'+ name + '/lint.js'); 106 | linter.run(); 107 | 108 | if (linter.failed) { 109 | linter.report(); 110 | break; 111 | } else { 112 | while (name.length < 32) name += ' '; 113 | this.step('\b\b - '+ name.capitalize() + '\u001B[32mOK\u001B[0m'); 114 | } 115 | } 116 | }); 117 | 118 | //////////////////////////////////////////////////////////////// 119 | // Minifying the widgets 120 | //////////////////////////////////////////////////////////////// 121 | task('build', 'Pack and minify the widgets', function() { 122 | Nake.run('pack', 'Packing the widget source files'); 123 | 124 | this.step('Minifying the source files'); 125 | for (var name in builds) { 126 | this.step('\b\b - '+ name.capitalize()); 127 | builds[name].compress(); 128 | } 129 | }); 130 | 131 | 132 | // manual kick-in in case the thing was called without 'nake' 133 | if (!on_npm) { 134 | Nake.start(); 135 | } 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome! 2 | 3 | This repository contains [RightJS](http://rightjs.org) common use plugins, 4 | you can find all the documentation, builds and so on at the official site 5 | over here: 6 | 7 | [RightJS Plugins](http://rightjs.org/plugins) 8 | 9 | 10 | ## Builds 11 | 12 | In order to build any of the plugins on your own you'll need 13 | [NodeJS](http://nodejs.org) and if you also have [npm](http://npmjs.org) 14 | you might want to install the [nake](https://github.com/MadRabbit/Nake) 15 | tools 16 | 17 | npm install nake 18 | 19 | After that either run `nake` 20 | 21 | nake build 22 | 23 | or, if you don't have [npm](http://npmjs.org), just run the `Nakefile` 24 | directly with [NodeJS](http://nodejs.org) 25 | 26 | node Nakefile build 27 | 28 | Try, `-l` or `--list` key to see which other tasks are available 29 | 30 | 31 | 32 | ## Options 33 | 34 | And you also can build/pack/check only some of the plugins 35 | 36 | nake build OPTIONS=dnd,keys 37 | 38 | 39 | 40 | ## License 41 | 42 | The code released under terms of the MIT License 43 | 44 | Copyright (C) 2009-2011 Nikolay Nemshilov 45 | -------------------------------------------------------------------------------- /dist/casting/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Dynamic Elements Casting v%{version} 3 | * http://rightjs.org/plugins/casting 4 | * 5 | * Copyright (C) 2010-2011 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/casting/layout.js: -------------------------------------------------------------------------------- 1 | (function(RightJS) { 2 | %{source_code} 3 | })(RightJS); -------------------------------------------------------------------------------- /dist/dnd/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Drag'n'Drop module v%{version} 3 | * http://rightjs.org/plugins/drag-n-drop 4 | * 5 | * Copyright (C) 2009-2012 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/dnd/layout.js: -------------------------------------------------------------------------------- 1 | (function(window, document, RightJS) { 2 | %{source_code} 3 | 4 | window.Draggable = RightJS.Draggable = Draggable; 5 | window.Droppable = RightJS.Droppable = Droppable; 6 | })(window, document, RightJS); -------------------------------------------------------------------------------- /dist/effects/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Additional Visual Effects v%{version} 3 | * http://rightjs.org/plugins/effects 4 | * 5 | * Copyright (C) 2009-2011 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/effects/layout.js: -------------------------------------------------------------------------------- 1 | (function(RightJS) { 2 | if (!RightJS.Fx) { throw "RightJS Fx is missing"; } 3 | 4 | %{source_code} 5 | })(RightJS); -------------------------------------------------------------------------------- /dist/effects/lint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Additional FX module JSLint check 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var okays = [ 7 | "Expected a 'break' statement before 'case'." 8 | ]; -------------------------------------------------------------------------------- /dist/jquerysh/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery-like interfaces v%{version} 3 | * http://rightjs.org/plugins/jquerysh 4 | * 5 | * Copyright (C) 2009-2011 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/jquerysh/layout.js: -------------------------------------------------------------------------------- 1 | (function(RightJS) { 2 | %{source_code} 3 | 4 | window.$ = window.jQuery = $; 5 | })(RightJS); -------------------------------------------------------------------------------- /dist/jquerysh/lint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The jquerysh module js-lint check 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var okays = [ 7 | "Expected a 'break' statement before 'case'." 8 | ]; -------------------------------------------------------------------------------- /dist/json/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JSON support module v%{version} 3 | * http://rightjs.org/plugins/json 4 | * 5 | * Copyright (C) 2009-2011 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/json/layout.js: -------------------------------------------------------------------------------- 1 | var JSON = function(RightJS, window) { 2 | 3 | %{source_code} 4 | 5 | return JSON; 6 | }(RightJS, window); -------------------------------------------------------------------------------- /dist/json/lint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JSLint check 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var okays = [ 7 | "Redefinition of 'JSON'." 8 | ]; 9 | -------------------------------------------------------------------------------- /dist/keys/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Pretty Keyboard Events v%{version} 3 | * http://rightjs.org/plugins/keys 4 | * 5 | * Copyright (C) 2009-2011 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/keys/layout.js: -------------------------------------------------------------------------------- 1 | (function(RightJS) { 2 | %{source_code} 3 | })(RightJS); -------------------------------------------------------------------------------- /dist/rails/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RubyOnRails Support Module v%{version} 3 | * http://rightjs.org/plugins/rails 4 | * 5 | * Copyright (C) 2009-2012 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/rails/layout.js: -------------------------------------------------------------------------------- 1 | (function(window, document, RightJS) { 2 | %{source_code} 3 | 4 | window.RR = RR; 5 | })(window, document, RightJS); -------------------------------------------------------------------------------- /dist/sizzle/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sizzle Engine Support v%{version} 3 | * http://rightjs.org/plugins/sizzle 4 | * 5 | * Copyright (C) 2009-2011 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/sizzle/layout.js: -------------------------------------------------------------------------------- 1 | %{source_code} 2 | -------------------------------------------------------------------------------- /dist/sizzle/lint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sizzle lint fixes 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | var okays = [ 7 | "Use '===' to compare with 'null'.", 8 | "Use '!==' to compare with 'null'.", 9 | " elem.parentNode.selectedIndex;", 10 | " node = elem;", 11 | " Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;" 12 | ]; -------------------------------------------------------------------------------- /dist/table/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tables Specific Wrappers v%{version} 3 | * http://rightjs.org/plugins/table 4 | * 5 | * Copyright (C) 2009-2011 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/table/layout.js: -------------------------------------------------------------------------------- 1 | var Table = RightJS.Table = (function(RightJS) { 2 | %{source_code} 3 | return Table; 4 | })(RightJS); -------------------------------------------------------------------------------- /src/casting/__init__.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Casting plugin initialization script 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | 7 | RightJS.Casting = { 8 | version: '2.2.0' 9 | }; 10 | 11 | include_module_files('casting'); -------------------------------------------------------------------------------- /src/casting/casting.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Advanced Elements typecasting feature. 3 | * Basically it allows you to handle all sorts of css-rules with dom-wrappers 4 | * 5 | * USAGE: 6 | * 7 | * var MyClass = Element.Wrappers.add('div#boo', new Class(Element, { 8 | * // some methods in here 9 | * })); 10 | * var MyClass = Element.Wrappers.add('div.hoo', new Class(Input, { 11 | * // some methods in here 12 | * })); 13 | * 14 | * Element.Wrappers.remove('div#boo'); 15 | * Element.Wrappers.remove(MyClass); 16 | * 17 | * 18 | * Copyright (C) 2010-2011 Alexey Dubinin 19 | * Copyright (C) 2010-2011 Nikolay Nemshilov 20 | */ 21 | 22 | var id_matchers = null, 23 | class_matchers = null, 24 | Wrappers = RightJS.Element.Wrappers; 25 | 26 | RightJS.$ext(Wrappers, { 27 | 28 | /** 29 | * Register a new wrapper for given css-rule 30 | * 31 | * @param String css-rule 32 | * @param RightJS.Element subclass 33 | * @return Element.Wrappers object 34 | */ 35 | set: function(css_rule, klass) { 36 | var match = css_rule.match(/^[a-z]+$/i); 37 | 38 | if (match) { // Tag-name 39 | Wrappers[css_rule.toUpperCase()] = klass; 40 | } else if ((match = css_rule.match(/^([a-z]*)\#[a-z0-9_\-]+$/i))) { 41 | if (id_matchers === null) { id_matchers = {}; } 42 | id_matchers[css_rule] = klass; 43 | } else if ((match = css_rule.match(/^([a-z]*)\.[a-z0-9_\-]+$/i))) { 44 | if (class_matchers === null) { class_matchers = {}; } 45 | class_matchers[css_rule] = klass; 46 | } 47 | 48 | return klass; 49 | }, 50 | 51 | /** 52 | * Returns a registered wrapper by a css-rule 53 | * 54 | * @param String css_rule 55 | * @return RightJS.Element or null 56 | */ 57 | get: function(css_rule) { 58 | var result = null; 59 | 60 | if (typeof css_rule === 'string') { 61 | if (css_rule.toUpperCase() in Wrappers) { 62 | result = Wrappers[css_rule.toUpperCase()]; 63 | } else if (id_matchers !== null && css_rule in id_matchers) { 64 | result = id_matchers[css_rule]; 65 | } else if (class_matchers !== null && css_rule in class_matchers) { 66 | result = class_matchers[css_rule]; 67 | } 68 | } else { 69 | result = RightJS([]); 70 | RightJS([Wrappers, id_matchers || {}, class_matchers || {}]).each(function(hash) { 71 | for (var key in hash) { 72 | if (hash[key] === css_rule) { 73 | result.push(key); 74 | } 75 | } 76 | }); 77 | 78 | result = result.compact(); 79 | 80 | if (result.empty()) { 81 | result = null; 82 | } 83 | } 84 | 85 | return result; 86 | }, 87 | 88 | /** 89 | * Checks if the css-rule is registered 90 | * 91 | * @param String css_rule 92 | * @return Boolean check result 93 | */ 94 | has: function(css_rule) { 95 | return Wrappers.get(css_rule) !== null; 96 | }, 97 | 98 | /** 99 | * Removes the dom-wrapper 100 | * 101 | * @param String css-rule or RightJS.Element class 102 | * @return Element.Wrappers object 103 | */ 104 | remove: function(css_rule) { 105 | RightJS([Wrappers, id_matchers || {}, class_matchers || {}]).each(function(object) { 106 | for (var key in object) { 107 | if (css_rule === key.toLowerCase() || object[key] === css_rule) { 108 | delete(object[key]); 109 | } 110 | } 111 | }); 112 | 113 | return Wrappers; 114 | } 115 | }); 116 | 117 | 118 | /** 119 | * Replacing the original casting method 120 | * with a new one that supporst all the other types of casting 121 | * 122 | * @param HTMLElement raw dom-element 123 | * @return Function wrapper class or undefined 124 | */ 125 | RightJS.Wrapper.Cast = function(element) { 126 | var key, tag = element.tagName; 127 | 128 | if (id_matchers !== null && element.id) { 129 | key = tag.toLowerCase() + '#'+ element.id; 130 | if (key in id_matchers) { 131 | return id_matchers[key]; 132 | } 133 | 134 | key = '#'+ element.id; 135 | if (key in id_matchers) { 136 | return id_matchers[key]; 137 | } 138 | } 139 | 140 | if (class_matchers !== null && element.className) { 141 | var classes = element.className.split(/\s+/), i=0, 142 | l_tag = tag.toLowerCase(); 143 | 144 | for (; i < classes.length; i++) { 145 | key = l_tag + "." + classes[i]; 146 | if (key in class_matchers) { 147 | return class_matchers[key]; 148 | } 149 | 150 | key = "." + classes[i]; 151 | if (key in class_matchers) { 152 | return class_matchers[key]; 153 | } 154 | } 155 | } 156 | 157 | if (tag in Wrappers) { 158 | return Wrappers[tag]; 159 | } 160 | 161 | return undefined; 162 | }; 163 | -------------------------------------------------------------------------------- /src/dnd/__init__.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The DND module initialization script 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | var R = RightJS, 7 | $ = RightJS.$, 8 | $w = RightJS.$w, 9 | Class = RightJS.Class, 10 | isHash = RightJS.isHash, 11 | isArray = RightJS.isArray, 12 | Element = RightJS.Element, 13 | Observer = RightJS.Observer; 14 | 15 | include_module_files( 16 | 'draggable', 17 | 'droppable', 18 | 'document', 19 | 'element' 20 | ); -------------------------------------------------------------------------------- /src/dnd/document.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The document events hooker 3 | * 4 | * Copyright (C) 2009-2012 Nikolay Nemshilov 5 | */ 6 | $(document).on({ 7 | // parocesses the automatically discovered elements 8 | ready: function() { 9 | Draggable.rescan(); 10 | Droppable.rescan(); 11 | }, 12 | 13 | mousemove: document_mousemove, 14 | touchmove: document_mousemove, 15 | 16 | mouseup: document_mouseup, 17 | touchend: document_mouseup 18 | }); 19 | 20 | 21 | // watch the draggables moving arond 22 | function document_mousemove(event) { 23 | if (Draggable.current !== null) { 24 | Draggable.current.dragProcess(event); 25 | Droppable.checkHover(event, Draggable.current); 26 | } 27 | } 28 | 29 | // releases the current draggable on mouse up 30 | function document_mouseup(event) { 31 | if (Draggable.current !== null) { 32 | Draggable.current.dragStop(event); 33 | } 34 | } -------------------------------------------------------------------------------- /src/dnd/draggable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Draggable unit 3 | * 4 | * Copyright (C) 2009-2012 Nikolay Nemshilov 5 | */ 6 | var Draggable = new Class(Observer, { 7 | extend: { 8 | version: '2.2.4', 9 | 10 | EVENTS: $w('before start drag stop drop'), 11 | 12 | Options: { 13 | handle: null, // a handle element that will start the drag 14 | 15 | snap: 0, // a number in pixels or [x,y] 16 | axis: null, // null or 'x' or 'y' or 'vertical' or 'horizontal' 17 | range: null, // {x: [min, max], y:[min, max]} or reference to another element 18 | 19 | dragClass: 'dragging', // the in-process class name 20 | 21 | clone: false, // if should keep a clone in place 22 | revert: false, // marker if the object should be moved back on finish 23 | revertDuration: 'normal', // the moving back fx duration 24 | 25 | scroll: true, // if it should automatically scroll 26 | scrollSensitivity: 32, // the scrolling area size in pixels 27 | 28 | zIndex: 10000000, // the element's z-index 29 | moveOut: false, // marker if the draggable should be moved out of it's context (for overflown elements) 30 | 31 | relName: 'draggable' // the audodiscovery feature key 32 | }, 33 | 34 | // referenece to the currently active draggable 35 | current: null, 36 | 37 | // scans the document for auto-processed draggables with the rel="draggable" attribute 38 | rescan: function(scope) { 39 | var key = this.Options.relName, ref = this === Draggable ? 'draggable' : 'droppable'; 40 | 41 | ($(scope)||$(document)).find('*[rel^="'+key+'"]').each(function(element) { 42 | if (!element[ref]) { 43 | new this(element, new Function('return '+element.get('data-'+key))() || {}); 44 | } 45 | }, this); 46 | } 47 | }, 48 | 49 | /** 50 | * Basic controller 51 | * 52 | * @param mixed element reference 53 | * @param Object options 54 | */ 55 | initialize: function(element, options) { 56 | this.element = $(element); 57 | this.$super(options); 58 | 59 | this._dragStart = R(function(event) { 60 | if (event.which === 1) { 61 | this.dragStart(event); 62 | } 63 | }).bind(this); 64 | this.handle.on({ 65 | mousedown: this._dragStart, 66 | touchstart: this._dragStart 67 | }); 68 | 69 | this.element.draggable = this; 70 | }, 71 | 72 | /** 73 | * detaches the mouse observers out of the draggable element 74 | * 75 | * @return this 76 | */ 77 | destroy: function() { 78 | this.handle 79 | .stopObserving('mousedown', this._dragStart) 80 | .stopObserving('touchstart', this._dragStart); 81 | delete(this.element.draggable); 82 | 83 | return this; 84 | }, 85 | 86 | // additional options processing 87 | setOptions: function(options) { 88 | this.$super(options); 89 | 90 | // checking the handle 91 | this.handle = this.options.handle ? $(this.options.handle) : this.element; 92 | 93 | // checking the spappings 94 | if (isArray(this.options.snap)) { 95 | this.snapX = this.options.snap[0]; 96 | this.snapY = this.options.snap[1]; 97 | } else { 98 | this.snapX = this.snapY = this.options.snap; 99 | } 100 | 101 | return this; 102 | }, 103 | 104 | /** 105 | * Moves the element back to the original position 106 | * 107 | * @return this 108 | */ 109 | revert: function() { 110 | var position = this.clone.position(); 111 | var end_style = { 112 | top: (position.y + this.ryDiff) + 'px', 113 | left: (position.x + this.rxDiff) + 'px' 114 | }; 115 | 116 | if (this.options.revertDuration && this.element.morph) { 117 | this.element.morph(end_style, { 118 | duration: this.options.revertDuration, 119 | onFinish: R(this.swapBack).bind(this) 120 | }); 121 | } else { 122 | this.element.setStyle(end_style); 123 | this.swapBack(); 124 | } 125 | 126 | return this; 127 | }, 128 | 129 | // protected 130 | 131 | // handles the event start 132 | dragStart: function(event) { 133 | if (this._drag) { return false; } else { this._drag = true; } 134 | 135 | this.fire('before', this, event.stop()); 136 | 137 | // calculating the positions diff 138 | var position = this.element.position(); 139 | 140 | this.xDiff = event.pageX - position.x; 141 | this.yDiff = event.pageY - position.y; 142 | 143 | // grabbing the relative position diffs for nested spaces 144 | this.rxDiff = this.ryDiff = 0; 145 | this.element.parents().reverse().each(function(parent) { 146 | if (parent.getStyle('position') !== 'static') { 147 | parent = parent.position(); 148 | 149 | this.rxDiff = - parent.x; 150 | this.ryDiff = - parent.y; 151 | } 152 | }, this); 153 | 154 | // preserving the element sizes 155 | var size = { 156 | x: this.element.getStyle('width'), 157 | y: this.element.getStyle('height') 158 | }; 159 | 160 | if (size.x == 'auto') { size.x = this.element._.offsetWidth + 'px'; } 161 | if (size.y == 'auto') { size.y = this.element._.offsetHeight + 'px'; } 162 | 163 | // building a clone element if necessary 164 | if (this.options.clone || this.options.revert) { 165 | this.clone = new Element(this.element._.cloneNode(true)).setStyle({ 166 | visibility: this.options.clone ? 'visible' : 'hidden' 167 | }).insertTo(this.element, 'before'); 168 | } 169 | 170 | // reinserting the element to the body so it was over all the other elements 171 | this.element.setStyle({ 172 | position: 'absolute', 173 | zIndex: Draggable.Options.zIndex++, 174 | top: (position.y + this.ryDiff) + 'px', 175 | left: (position.x + this.rxDiff) + 'px', 176 | width: size.x, 177 | height: size.y 178 | }).addClass(this.options.dragClass); 179 | 180 | if (this.options.moveOut) { 181 | this.element.insertTo(document.body); 182 | } 183 | 184 | // caching the window scrolls 185 | this.winScrolls = $(window).scrolls(); 186 | this.winSizes = $(window).size(); 187 | 188 | Draggable.current = this.calcConstraints().fire('start', this, event); 189 | 190 | this.style = this.element._.style; 191 | }, 192 | 193 | // catches the mouse move event 194 | dragProcess: function(event) { 195 | var page_x = event.pageX, page_y = event.pageY, x = page_x - this.xDiff, y = page_y - this.yDiff; 196 | 197 | // checking the range 198 | if (this.ranged) { 199 | if (this.minX > x) { x = this.minX; } 200 | if (this.maxX < x) { x = this.maxX; } 201 | if (this.minY > y) { y = this.minY; } 202 | if (this.maxY < y) { y = this.maxY; } 203 | } 204 | 205 | // checking the scrolls 206 | if (this.options.scroll) { 207 | var scrolls = {x: this.winScrolls.x, y: this.winScrolls.y}, 208 | sensitivity = this.options.scrollSensitivity; 209 | 210 | if ((page_y - scrolls.y) < sensitivity) { 211 | scrolls.y = page_y - sensitivity; 212 | } else if ((scrolls.y + this.winSizes.y - page_y) < sensitivity){ 213 | scrolls.y = page_y - this.winSizes.y + sensitivity; 214 | } 215 | 216 | if ((page_x - scrolls.x) < sensitivity) { 217 | scrolls.x = page_x - sensitivity; 218 | } else if ((scrolls.x + this.winSizes.x - page_x) < sensitivity){ 219 | scrolls.x = page_x - this.winSizes.x + sensitivity; 220 | } 221 | 222 | if (scrolls.y < 0) { scrolls.y = 0; } 223 | if (scrolls.x < 0) { scrolls.x = 0; } 224 | 225 | if (scrolls.y < this.winScrolls.y || scrolls.y > this.winScrolls.y || 226 | scrolls.x < this.winScrolls.x || scrolls.x > this.winScrolls.x) { 227 | 228 | $(window).scrollTo(this.winScrolls = scrolls); 229 | } 230 | } 231 | 232 | // checking the snaps 233 | if (this.snapX) { x = x - x % this.snapX; } 234 | if (this.snapY) { y = y - y % this.snapY; } 235 | 236 | // checking the constraints 237 | if (!this.axisY) { this.style.left = (x + this.rxDiff) + 'px'; } 238 | if (!this.axisX) { this.style.top = (y + this.ryDiff) + 'px'; } 239 | 240 | this.fire('drag', this, event); 241 | }, 242 | 243 | // handles the event stop 244 | dragStop: function(event) { 245 | this.element.removeClass(this.options.dragClass); 246 | 247 | // notifying the droppables for the drop 248 | Droppable.checkDrop(event, this); 249 | 250 | if (this.options.revert) { 251 | this.revert(); 252 | } else { 253 | this._drag = false; 254 | } 255 | 256 | Draggable.current = null; 257 | 258 | this.fire('stop', this, event); 259 | }, 260 | 261 | // swaps the clone element to the actual element back 262 | swapBack: function() { 263 | if (this.clone) { 264 | this.clone.replace( 265 | this.element.setStyle({ 266 | width: this.clone.getStyle('width'), 267 | height: this.clone.getStyle('height'), 268 | position: this.clone.getStyle('position'), 269 | zIndex: this.clone.getStyle('zIndex') || '' 270 | }) 271 | ); 272 | } 273 | this._drag = false; 274 | }, 275 | 276 | // calculates the constraints 277 | calcConstraints: function() { 278 | var axis = this.options.axis; 279 | this.axisX = R(['x', 'horizontal']).include(axis); 280 | this.axisY = R(['y', 'vertical']).include(axis); 281 | 282 | this.ranged = false; 283 | var range = this.options.range; 284 | if (range) { 285 | this.ranged = true; 286 | 287 | // if the range is defined by another element 288 | var element = $(range); 289 | if (element instanceof Element) { 290 | var dims = element.dimensions(); 291 | 292 | range = { 293 | x: [dims.left, dims.left + dims.width], 294 | y: [dims.top, dims.top + dims.height] 295 | }; 296 | } 297 | 298 | if (isHash(range)) { 299 | var size = this.element.size(); 300 | 301 | if (range.x) { 302 | this.minX = range.x[0]; 303 | this.maxX = range.x[1] - size.x; 304 | } 305 | if (range.y) { 306 | this.minY = range.y[0]; 307 | this.maxY = range.y[1] - size.y; 308 | } 309 | } 310 | } 311 | 312 | return this; 313 | } 314 | }); -------------------------------------------------------------------------------- /src/dnd/droppable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Droppable unit 3 | * 4 | * Copyright (C) 2009-2010 Nikolay Nemshilov 5 | */ 6 | var Droppable = new Class(Observer, { 7 | extend: { 8 | EVENTS: $w('drop hover leave'), 9 | 10 | Options: { 11 | accept: '*', 12 | containment: null, // the list of elements (or ids) that should to be accepted 13 | 14 | overlap: null, // 'x', 'y', 'horizontal', 'vertical', 'both' makes it respond only if the draggable overlaps the droppable 15 | overlapSize: 0.5, // the overlapping level 0 for nothing 1 for the whole thing 16 | 17 | allowClass: 'droppable-allow', 18 | denyClass: 'droppable-deny', 19 | 20 | relName: 'droppable' // automatically discovered feature key 21 | }, 22 | 23 | // See the Draggable rescan method, case we're kinda hijacking it in here 24 | rescan: Draggable.rescan, 25 | 26 | /** 27 | * Checks for hoverting draggable 28 | * 29 | * @param Event mouse event 30 | * @param Draggable draggable 31 | */ 32 | checkHover: function(event, draggable) { 33 | for (var i=0, length = this.active.length; i < length; i++) { 34 | this.active[i].checkHover(event, draggable); 35 | } 36 | }, 37 | 38 | /** 39 | * Checks for a drop 40 | * 41 | * @param Event mouse event 42 | * @param Draggable draggable 43 | */ 44 | checkDrop: function(event, draggable) { 45 | for (var i=0, length = this.active.length; i < length; i++) { 46 | this.active[i].checkDrop(event, draggable); 47 | } 48 | }, 49 | 50 | active: [] 51 | }, 52 | 53 | /** 54 | * Basic cosntructor 55 | * 56 | * @param mixed the draggable element reference 57 | * @param Object options 58 | */ 59 | initialize: function(element, options) { 60 | this.element = $(element); 61 | this.$super(options); 62 | 63 | Droppable.active.push(this.element._droppable = this); 64 | }, 65 | 66 | /** 67 | * Detaches the attached events 68 | * 69 | * @return self 70 | */ 71 | destroy: function() { 72 | Droppable.active = Droppable.active.without(this); 73 | delete(this.element.droppable); 74 | return this; 75 | }, 76 | 77 | /** 78 | * checks the event for hovering 79 | * 80 | * @param Event mouse event 81 | * @param Draggable the draggable object 82 | */ 83 | checkHover: function(event, draggable) { 84 | if (this.hoveredBy(event, draggable)) { 85 | if (!this._hovered) { 86 | this._hovered = true; 87 | this.element.addClass(this.options[this.allows(draggable) ? 'allowClass' : 'denyClass']); 88 | this.fire('hover', draggable, this, event); 89 | } 90 | } else if (this._hovered) { 91 | this._hovered = false; 92 | this.reset().fire('leave', draggable, this, event); 93 | } 94 | }, 95 | 96 | /** 97 | * Checks if it should process the drop from draggable 98 | * 99 | * @param Event mouse event 100 | * @param Draggable draggable 101 | */ 102 | checkDrop: function(event, draggable) { 103 | this.reset(); 104 | if (this.hoveredBy(event, draggable) && this.allows(draggable)) { 105 | draggable.fire('drop', this, draggable, event); 106 | this.fire('drop', draggable, this, event); 107 | } 108 | }, 109 | 110 | /** 111 | * resets the element state 112 | * 113 | * @return self 114 | */ 115 | reset: function() { 116 | this.element.removeClass(this.options.allowClass).removeClass(this.options.denyClass); 117 | return this; 118 | }, 119 | 120 | // protected 121 | 122 | // checks if the element is hovered by the event 123 | hoveredBy: function(event, draggable) { 124 | var dims = this.element.dimensions(), 125 | t_top = dims.top, 126 | t_left = dims.left, 127 | t_right = dims.left + dims.width, 128 | t_bottom = dims.top + dims.height, 129 | event_x = event.pageX, 130 | event_y = event.pageY; 131 | 132 | // checking the overlapping 133 | if (this.options.overlap) { 134 | var drag_dims = draggable.element.dimensions(), 135 | level = this.options.overlapSize, 136 | top = drag_dims.top, 137 | left = drag_dims.left, 138 | right = drag_dims.left + drag_dims.width, 139 | bottom = drag_dims.top + drag_dims.height; 140 | 141 | 142 | switch (this.options.overlap) { 143 | // horizontal overlapping only check 144 | case 'x': 145 | case 'horizontal': 146 | return ( 147 | (top > t_top && top < t_bottom) || 148 | (bottom > t_top && bottom < t_bottom) 149 | ) && ( 150 | (left > t_left && left < (t_right - dims.width * level)) || 151 | (right < t_right && right > (t_left + dims.width * level)) 152 | ); 153 | 154 | // vertical overlapping only check 155 | case 'y': 156 | case 'vertical': 157 | return ( 158 | (left > t_left && left < t_right) || 159 | (right > t_left && right < t_right) 160 | ) && ( 161 | (top > t_top && top < (t_bottom - dims.height * level)) || 162 | (bottom < t_bottom && bottom > (t_top + dims.height * level)) 163 | ); 164 | 165 | // both overlaps check 166 | default: 167 | return ( 168 | (left > t_left && left < (t_right - dims.width * level)) || 169 | (right < t_right && right > (t_left + dims.width * level)) 170 | ) && ( 171 | (top > t_top && top < (t_bottom - dims.height * level)) || 172 | (bottom < t_bottom && bottom > (t_top + dims.height * level)) 173 | ); 174 | } 175 | 176 | } else { 177 | // simple check agains the event position 178 | return event_x > t_left && event_x < t_right && event_y > t_top && event_y < t_bottom; 179 | } 180 | }, 181 | 182 | // checks if the object accepts the draggable 183 | allows: function(draggable) { 184 | if (this.options.containment && !this._scanned) { 185 | this.options.containment = R(this.options.containment).map($); 186 | this._scanned = true; 187 | } 188 | 189 | // checking the invitations list 190 | var welcomed = this.options.containment ? this.options.containment.includes(draggable.element) : true; 191 | 192 | return welcomed && (this.options.accept == '*' ? true : draggable.element.match(this.options.accept)); 193 | } 194 | 195 | }); -------------------------------------------------------------------------------- /src/dnd/element.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Element level hooks for drag'n'drops 3 | * 4 | * Copyright (C) 2009-2010 Nikolay Nemshilov 5 | */ 6 | Element.include({ 7 | 8 | makeDraggable: function(options) { 9 | new Draggable(this, options); 10 | return this; 11 | }, 12 | 13 | undoDraggable: function() { 14 | if ('draggable' in this) { 15 | this.draggable.destroy(); 16 | } 17 | 18 | return this; 19 | }, 20 | 21 | makeDroppable: function(options) { 22 | new Droppable(this, options); 23 | return this; 24 | }, 25 | 26 | undoDroppable: function() { 27 | if ('droppable' in this) { 28 | this.droppable.destroy(); 29 | } 30 | 31 | return this; 32 | } 33 | 34 | }); -------------------------------------------------------------------------------- /src/effects/__init__.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The plugin initializtion script 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | 7 | var R = RightJS, 8 | $ = RightJS.$, 9 | $w = RightJS.$w, 10 | $A = RightJS.$A, 11 | Fx = RightJS.Fx, 12 | Class = RightJS.Class, 13 | Object = RightJS.Object, 14 | Element = RightJS.Element, 15 | defined = RightJS.defined, 16 | isHash = RightJS.isHash, 17 | isString = RightJS.isString; 18 | 19 | RightJS.Effects = { 20 | version: '2.2.0' 21 | }; 22 | 23 | include_module_files( 24 | 'fx/move', 25 | 'fx/zoom', 26 | 'fx/bounce', 27 | 'fx/run', 28 | 'fx/puff', 29 | 'fx/glow', 30 | 'element' 31 | ); -------------------------------------------------------------------------------- /src/effects/element.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Element shortcuts for the additional effects 3 | * 4 | * @copyright (C) 2009-2011 Nikolay Nemshilov 5 | */ 6 | RightJS.Element.include({ 7 | /** 8 | * The move visual effect shortcut 9 | * 10 | * @param position Object end position x/y or top/left 11 | * @param options Object fx options 12 | * @return Element self 13 | */ 14 | move: function(position, options) { 15 | return call_fx(this, 'move', [position, options || {}]); // <- don't replace with arguments 16 | }, 17 | 18 | /** 19 | * The bounce effect shortcut 20 | * 21 | * @param Number optional bounce size 22 | * @param Object fx options 23 | * @return Element self 24 | */ 25 | bounce: function() { 26 | return call_fx(this, 'bounce', arguments); 27 | }, 28 | 29 | /** 30 | * The zoom effect shortcut 31 | * 32 | * @param mixed the zooming value, see Fx.Zoom#start options 33 | * @param Object fx options 34 | * @return Element self 35 | */ 36 | zoom: function(size, options) { 37 | return call_fx(this, 'zoom', [size, options || {}]); 38 | }, 39 | 40 | /** 41 | * Initiates the Fx.Run effect 42 | * 43 | * @param String running direction 44 | * @param Object fx options 45 | * @return Element self 46 | */ 47 | run: function() { 48 | return call_fx(this, 'run', arguments); 49 | }, 50 | 51 | /** 52 | * The puff effect shortcut 53 | * 54 | * @param String running direction in|out|toggle 55 | * @param Object fx options 56 | * @return Element self 57 | */ 58 | puff: function() { 59 | return call_fx(this, 'puff', arguments); 60 | }, 61 | 62 | /** 63 | * The Fx.Glow effect shortcut 64 | * 65 | * @param String optinal glow color 66 | * @param Object fx options 67 | * @return Element self 68 | */ 69 | glow: function() { 70 | return call_fx(this, 'glow', arguments); 71 | } 72 | }); 73 | 74 | /** 75 | * Runs Fx on the element 76 | * 77 | * @param Element element reference 78 | * @param String fx name 79 | * @param Array effect arguments 80 | * @return the element back 81 | */ 82 | function call_fx(element, name, params) { 83 | var args = $A(params).compact(), 84 | options = isHash(args.last()) ? args.pop() : {}, 85 | fx = new Fx[name.capitalize()](element, options); 86 | 87 | fx.start.apply(fx, args); 88 | 89 | return element; 90 | } 91 | -------------------------------------------------------------------------------- /src/effects/fx/bounce.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bounce visual effect, slightly moves an element forward and back 3 | * 4 | * @copyright (C) 2009 Nikolay V. Nemshilov 5 | */ 6 | Fx.Bounce = new Class(Fx, { 7 | extend: { 8 | Options: Object.merge(Fx.Options, { 9 | duration: 'short', 10 | direction: 'top', 11 | value: 16 // the shake distance 12 | }) 13 | }, 14 | 15 | prepare: function(value) { 16 | value = value || this.options.value; 17 | 18 | var position = this.element.position(); 19 | var duration = Fx.Durations[this.options.duration] || this.options.duration; 20 | var move_options = {duration: duration, position: 'relative'}; 21 | 22 | var key = 'y'; // top bounce by default 23 | 24 | switch (this.options.direction) { 25 | case 'right': 26 | value = -value; 27 | case 'left': 28 | key = 'x'; 29 | break; 30 | case 'bottom': 31 | value = -value; 32 | } 33 | 34 | var up_pos = {}, down_pos = {}; 35 | up_pos[key] = -value; 36 | down_pos[key] = value; 37 | 38 | new Fx.Move(this.element, move_options).start(up_pos); 39 | new Fx.Move(this.element, move_options).start(down_pos); 40 | 41 | this.finish.bind(this).delay(1); 42 | 43 | return this; 44 | } 45 | }); -------------------------------------------------------------------------------- /src/effects/fx/glow.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Glow effect, kinda the same thing as Hightlight, but changes the text color 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | Fx.Glow = new Class(Fx.Morph, { 7 | extend: { 8 | Options: Object.merge(Fx.Options, { 9 | color: '#FF8', 10 | transition: 'Exp' 11 | }) 12 | }, 13 | 14 | // protected 15 | 16 | /** 17 | * starts the transition 18 | * 19 | * @param high String the hightlight color 20 | * @param back String optional fallback color 21 | * @return self 22 | */ 23 | prepare: function(start, end) { 24 | var element = this.element, 25 | element_style = element._.style, 26 | style_name = 'color', 27 | end_color = end || element.getStyle(style_name); 28 | 29 | // trying to find the end color 30 | end_color = [element].concat(element.parents()) 31 | .map('getStyle', style_name) 32 | .compact().first() || '#FFF'; 33 | 34 | element_style[style_name] = (start || this.options.color); 35 | 36 | return this.$super({color: end_color}); 37 | } 38 | }); -------------------------------------------------------------------------------- /src/effects/fx/move.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The basic move visual effect 3 | * 4 | * @copyright (C) 2009-2010 Nikolay V. Nemshilov 5 | */ 6 | Fx.Move = new Class(Fx.Morph, { 7 | extend: { 8 | Options: Object.merge(Fx.Options, { 9 | duration: 'long', 10 | position: 'absolute' // <- defines the positions measurment principle, not the element positioning 11 | }) 12 | }, 13 | 14 | prepare: function(end_position) { 15 | return this.$super(this.getEndPosition(end_position)); 16 | }, 17 | 18 | // moved to a separated method to be able to call it from subclasses 19 | getEndPosition: function(end_position) { 20 | var position = this.element.getStyle('position'), end_style = {}; 21 | 22 | if (position != 'absolute' || position != 'relative') { 23 | this.element._.style.position = position = position == 'fixed' ? 'absolute' : 'relative'; 24 | } 25 | 26 | if (end_position.top) { end_position.y = end_position.top.toInt(); } 27 | if (end_position.left) { end_position.x = end_position.left.toInt(); } 28 | 29 | // adjusting the end position 30 | var cur_position = this.element.position(); 31 | var par_position = this.getParentPosition(); 32 | var rel_left = cur_position.x - par_position.x; 33 | var rel_top = cur_position.y - par_position.y; 34 | 35 | if (this.options.position == 'relative') { 36 | if (position == 'absolute') { 37 | if (defined(end_position.x)) { end_position.x += cur_position.x; } 38 | if (defined(end_position.y)) { end_position.y += cur_position.x; } 39 | } else { 40 | if (defined(end_position.x)) { end_position.x += rel_left; } 41 | if (defined(end_position.y)) { end_position.y += rel_top; } 42 | } 43 | } else if (position == 'relative') { 44 | if (defined(end_position.x)) { end_position.x += rel_left - cur_position.x; } 45 | if (defined(end_position.y)) { end_position.y += rel_top - cur_position.y; } 46 | } 47 | 48 | // need this to bypass the other styles from the subclasses 49 | for (var key in end_position) { 50 | switch (key) { 51 | case 'top': case 'left': break; 52 | case 'y': end_style.top = end_position.y + 'px'; break; 53 | case 'x': end_style.left = end_position.x + 'px'; break; 54 | default: end_style[key] = end_position[key]; 55 | } 56 | } 57 | 58 | return end_style; 59 | }, 60 | 61 | getParentPosition: function() { 62 | Fx.Move.Dummy = Fx.Move.Dummy || new Element('div', {style: 'width:0;height:0;visibility:hidden'}); 63 | this.element.insert(Fx.Move.Dummy, 'before'); 64 | var position = Fx.Move.Dummy.position(); 65 | Fx.Move.Dummy.remove(); 66 | return position; 67 | } 68 | }); -------------------------------------------------------------------------------- /src/effects/fx/puff.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The puff visual effect 3 | * 4 | * Copyright (C) 2009-2010 Nikolay V. Nemshilov 5 | */ 6 | Fx.Puff = new Class(Fx.Zoom, { 7 | extend: { 8 | Options: Object.merge(Fx.Zoom.Options, { 9 | size: 1.4 // the end/initial size of the element 10 | }) 11 | }, 12 | 13 | // protected 14 | 15 | prepare: function(in_how) { 16 | var how = in_how || 'toggle', opacity = 0, size = this.options.size, initial_style; 17 | 18 | if (how == 'out' || (how == 'toggle' && this.element.visible())) { 19 | initial_style = this.getEndPosition(this._getZoomedStyle(1)); 20 | this.onFinish(function() { 21 | initial_style.opacity = 1; 22 | this.element.hide().setStyle(initial_style); 23 | }); 24 | 25 | } else { 26 | this.element.setStyle('visibility: visible').show(); 27 | 28 | var width = this.element.offsetWidth; 29 | initial_style = this.getEndPosition(this._getZoomedStyle(1)); 30 | 31 | this.onFinish(function() { 32 | this.element.setStyle(initial_style); 33 | }); 34 | 35 | this.element.setStyle(Object.merge( 36 | this.getEndPosition(this._getZoomedStyle(size)), { 37 | opacity: 0, 38 | visibility: 'visible' 39 | } 40 | )); 41 | 42 | size = width / this.element.offsetWidth; 43 | opacity = 1; 44 | } 45 | 46 | 47 | return this.$super(size, {opacity: opacity}); 48 | } 49 | 50 | }); -------------------------------------------------------------------------------- /src/effects/fx/run.js: -------------------------------------------------------------------------------- 1 | /** 2 | * run out and run in efffects 3 | * 4 | * Copyright (C) 2009-2010 Nikolay V. Nemshilov 5 | */ 6 | Fx.Run = new Class(Fx.Move, { 7 | extend: { 8 | Options: Object.merge(Fx.Move.Options, { 9 | direction: 'left' 10 | }) 11 | }, 12 | 13 | prepare: function(in_how) { 14 | var how = in_how || 'toggle', position = {}, dimensions = this.element.dimensions(), threshold = 80; 15 | 16 | if (how == 'out' || (how == 'toggle' && this.element.visible())) { 17 | if (this.options.direction == 'left') { 18 | position.x = -dimensions.width - threshold; 19 | } else { 20 | position.y = -dimensions.height - threshold; 21 | } 22 | this.onFinish(function() { 23 | this.element.hide().setStyle(this.getEndPosition({x: dimensions.left, y: dimensions.top})); 24 | }); 25 | } else { 26 | dimensions = this.element.setStyle('visibility: hidden').show().dimensions(); 27 | var pre_position = {}; 28 | 29 | if (this.options.direction == 'left') { 30 | pre_position.x = - dimensions.width - threshold; 31 | position.x = dimensions.left; 32 | } else { 33 | pre_position.y = - dimensions.height - threshold; 34 | position.y = dimensions.top; 35 | } 36 | 37 | this.element.setStyle(this.getEndPosition(pre_position)).setStyle('visibility: visible'); 38 | } 39 | 40 | return this.$super(position); 41 | } 42 | }); -------------------------------------------------------------------------------- /src/effects/fx/zoom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Zoom visual effect, graduately zoom and element in or out 3 | * 4 | * @copyright (C) 2009-2011 Nikolay V. Nemshilov 5 | */ 6 | Fx.Zoom = new Class(Fx.Move, { 7 | PROPERTIES: $w('width height lineHeight paddingTop paddingRight paddingBottom paddingLeft fontSize borderWidth'), 8 | 9 | extend: { 10 | Options: Object.merge(Fx.Move.Options, { 11 | position: 'relative', // overriding the Fx.Move default 12 | duration: 'normal', 13 | from: 'center' 14 | }) 15 | }, 16 | 17 | prepare: function(size, additional_styles) { 18 | return this.$super(this._getZoomedStyle(size, additional_styles)); 19 | }, 20 | 21 | // private 22 | 23 | // calculates the end zoommed style 24 | _getZoomedStyle: function(size, additional_styles) { 25 | var proportion = this._getProportion(size); 26 | 27 | return Object.merge( 28 | this._getBasicStyle(proportion), 29 | this._getEndPosition(proportion), 30 | additional_styles 31 | ); 32 | }, 33 | 34 | // calculates the zooming proportion 35 | _getProportion: function(size) { 36 | if (isHash(size)) { 37 | var dummy = $E('div').insertTo( 38 | $E('div', { 39 | style: "visibility:hidden;float:left;height:0;width:0" 40 | }).insertTo(document.body) 41 | ).setStyle(size); 42 | 43 | size = size.height ? 44 | dummy.size().y / this.element.size().y : 45 | dummy.size().x / this.element.size().x ; 46 | 47 | dummy.remove(); 48 | } else if (isString(size)) { 49 | size = R(size).endsWith('%') ? R(size).toFloat() / 100 : R(size).toFloat(); 50 | } 51 | 52 | return size; 53 | }, 54 | 55 | // getting the basic end style 56 | _getBasicStyle: function(proportion) { 57 | var style = clone_styles(this.element, this.PROPERTIES), re = /([\d\.]+)/g; 58 | 59 | function adjust_value(m) { 60 | return ''+ (R(m).toFloat() * proportion); 61 | } 62 | 63 | for (var key in style) { 64 | if (key === 'width' || key === 'height') { 65 | style[key] = style[key] || (this.element['offset'+R(key).capitalize()]+'px'); 66 | } 67 | 68 | if (style[key].match(re)) { 69 | style[key] = style[key].replace(re, adjust_value); 70 | } else { 71 | delete(style[key]); 72 | } 73 | } 74 | 75 | // preventing the border disappearance 76 | if (style.borderWidth && R(style.borderWidth).toFloat() < 1) { 77 | style.borderWidth = '1px'; 78 | } 79 | 80 | return style; 81 | }, 82 | 83 | // getting the position adjustments 84 | _getEndPosition: function(proportion) { 85 | var position = {}; 86 | var sizes = this.element.size(); 87 | var x_diff = sizes.x * (proportion - 1); 88 | var y_diff = sizes.y * (proportion - 1); 89 | 90 | switch (this.options.from.replace('-', ' ').split(' ').sort().join('_')) { 91 | case 'top': 92 | position.x = - x_diff / 2; 93 | break; 94 | 95 | case 'right': 96 | position.x = - x_diff; 97 | position.y = - y_diff / 2; 98 | break; 99 | 100 | case 'bottom': 101 | position.x = - x_diff / 2; 102 | case 'bottom_left': 103 | position.y = - y_diff; 104 | break; 105 | 106 | case 'bottom_right': 107 | position.y = - y_diff; 108 | case 'right_top': 109 | position.x = - x_diff; 110 | break; 111 | 112 | case 'center': 113 | position.x = - x_diff / 2; 114 | case 'left': 115 | position.y = - y_diff / 2; 116 | break; 117 | 118 | default: // left_top or none, do nothing, let the thing expand as is 119 | } 120 | 121 | return position; 122 | } 123 | }); 124 | 125 | function clone_styles(element, keys) { 126 | for (var i=0, len = keys.length, style = element.computedStyles(), clean = {}, key; i < len; i++) { 127 | key = keys[i]; 128 | if (key in style) { 129 | clean[key] = ''+ style[key]; 130 | } 131 | } 132 | 133 | return clean; 134 | } -------------------------------------------------------------------------------- /src/jquerysh/__init__.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jquerysh initialization script 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var _jQuery = window.jQuery, 7 | _$ = window.$, 8 | rjs_$ = RightJS.$, 9 | $$ = RightJS.$$, 10 | $E = RightJS.$E, 11 | $A = RightJS.$A, 12 | $ext = RightJS.$ext, 13 | Xhr = RightJS.Xhr, 14 | Browser = RightJS.Browser, 15 | Object = RightJS.Object; 16 | 17 | 18 | include_module_files( 19 | 'jquerysh', 20 | 'dollar', 21 | 'jquery', 22 | 'element', 23 | 'ajax' 24 | ); -------------------------------------------------------------------------------- /src/jquerysh/ajax.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery like ajax interfaces 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | $ext($, { 7 | 8 | param: function(object) { 9 | return Object.toQueryString(object); 10 | }, 11 | 12 | ajax: function(url, options) { 13 | options = options || {}; 14 | 15 | if (typeof(url) === 'string') { 16 | options.url = url; 17 | } else { 18 | options = url; 19 | } 20 | 21 | var xhr_options = {}; 22 | 23 | function callback(original, xhr) { 24 | original( 25 | options.dataType === 'json' ? xhr.json : xhr.text, 26 | xhr.successful() ? 'success' : 'error', 27 | xhr 28 | ); 29 | } 30 | 31 | if (options.success) { 32 | xhr_options.onSuccess = function() { 33 | callback(options.success, this); 34 | }; 35 | } 36 | 37 | if (options.error) { 38 | xhr_options.onFailure = function() { 39 | callback(options.error, this); 40 | }; 41 | } 42 | 43 | if (options.complete) { 44 | xhr_options.onComplete = function() { 45 | callback(options.complete, this); 46 | }; 47 | } 48 | 49 | xhr_options.method = options.type; 50 | 51 | if (options.headers) { 52 | xhr_options.headers = options.headers; 53 | } 54 | if (options.jsonp) { 55 | xhr_options.jsonp = options.jsonp; 56 | } 57 | if (options.url.indexOf('callback=?') > 0) { 58 | xhr_options.jsonp = true; 59 | options.url = options.url.replace(/(\?|\&)callback=\?/, ''); 60 | } 61 | 62 | return new Xhr(options.url, xhr_options).send(options.data); 63 | }, 64 | 65 | get: function() { 66 | return make_ajax_call({type: 'get'}, arguments); 67 | }, 68 | 69 | post: function(url, data, success, data_type) { 70 | return make_ajax_call({type: 'post'}, arguments); 71 | }, 72 | 73 | getJSON: function(url, data, success) { 74 | return make_ajax_call({dataType: 'json'}, arguments); 75 | }, 76 | 77 | getScript: function(url, success) { 78 | return make_ajax_call({dataType: 'script'}, arguments); 79 | } 80 | 81 | }); 82 | 83 | function make_ajax_call(opts, args) { 84 | return $.ajax($ext(ajax_options.apply(this, args), opts)); 85 | } 86 | 87 | function ajax_options(url, data, success, data_type) { 88 | if (typeof(data) === 'function') { 89 | data_type = success; 90 | success = data; 91 | data = undefined; 92 | } 93 | 94 | return { 95 | url: url, 96 | data: data, 97 | success: success, 98 | dataType: data_type 99 | }; 100 | } 101 | 102 | Xhr.include({ 103 | success: function(callback) { 104 | return this.on('success', callback); 105 | }, 106 | 107 | error: function(callback) { 108 | return this.on('failure', callback); 109 | }, 110 | 111 | complete: function(callback) { 112 | return this.on('complete', callback); 113 | } 114 | }); -------------------------------------------------------------------------------- /src/jquerysh/dollar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery-like '$' function behavior 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var $ = function(something) { 7 | switch(typeof something) { 8 | case 'string': 9 | var hash = something[0], id = something.substr(1); 10 | 11 | if (hash === '#' && (/^[\w\-]+$/).test(id)) { 12 | return rjs_$(id); 13 | } else if (hash === '<') { 14 | return $E('div', {html: something}).first(); 15 | } else { 16 | hash = $$(something); 17 | hash.cssRule = RightJS(something); 18 | return $ext(hash, RightJS.jQuerysh.collectionMethods); 19 | } 20 | 21 | case 'function': 22 | return rjs_$(document).onReady(something); 23 | 24 | default: 25 | return rjs_$(something); 26 | } 27 | }; -------------------------------------------------------------------------------- /src/jquerysh/element.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Element level jQuery like aliases 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | RightJS.Element.include({ 7 | 8 | appendTo: function(target) { 9 | return this.insertTo(target); 10 | }, 11 | 12 | prepend: function(content) { 13 | return this.insert(content, 'top'); 14 | }, 15 | 16 | before: function(content) { 17 | return this.insert(content, 'before'); 18 | }, 19 | 20 | after: function(content) { 21 | return this.insert(content, 'after'); 22 | }, 23 | 24 | insertBefore: function(target) { 25 | return this.insertTo(target, 'before'); 26 | }, 27 | 28 | attr: function(name, value) { 29 | return value === undefined ? this.get(name) : this.set(name, value); 30 | }, 31 | 32 | css: function(name, value) { 33 | return (typeof(name) === 'string' && value === undefined) ? 34 | this.getStyle(name) : this.setStyle(name, value); 35 | }, 36 | 37 | offset: function() { 38 | var position = this.position(); 39 | return { 40 | left: position.x, 41 | top: position.y 42 | }; 43 | }, 44 | 45 | width: function() { 46 | return this.size().x; 47 | }, 48 | 49 | height: function() { 50 | return this.size().y; 51 | }, 52 | 53 | scrollLeft: function() { 54 | return this.scrolls().x; 55 | }, 56 | 57 | scrollTop: function() { 58 | return this.scrolls().y; 59 | }, 60 | 61 | bind: function() { 62 | return this.on.apply(this, arguments); 63 | }, 64 | 65 | unbind: function() { 66 | return this.stopObserving.apply(this, arguments); 67 | }, 68 | 69 | trigger: function(name, options) { 70 | return this.fire(name, options); 71 | }, 72 | 73 | animate: function(style, time, finish) { 74 | return this.morph(style, {duration: time, onFinish: finish}); 75 | }, 76 | 77 | fadeIn: function() { 78 | return this.fade('in'); 79 | }, 80 | 81 | fadeOut: function() { 82 | return this.fade('out'); 83 | }, 84 | 85 | slideDown: function() { 86 | return this.slide('in'); 87 | }, 88 | 89 | slideUp: function() { 90 | return this.slide('out'); 91 | } 92 | 93 | }); -------------------------------------------------------------------------------- /src/jquerysh/jquery.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery core methods emulation 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | $ext($, { 7 | 8 | browser: { 9 | webkit: Browser.WebKit, 10 | opera: Browser.Opera, 11 | msie: Browser.IE, 12 | mozilla: Browser.Gecko 13 | }, 14 | 15 | // Type checks 16 | 17 | isFunction: function(value) { 18 | return RightJS.isFunction(value); 19 | }, 20 | 21 | isArray: function(value) { 22 | return RightJS.isArray(value); 23 | }, 24 | 25 | isPlainObject: function(value) { 26 | return RightJS.isHash(value); 27 | }, 28 | 29 | isEmptyObject: function(value) { 30 | return Object.empty(value); 31 | }, 32 | 33 | globalEval: function(script) { 34 | return RightJS.$eval(script); 35 | }, 36 | 37 | // Array stuff 38 | 39 | makeArray: function(value) { 40 | return $A(value); 41 | }, 42 | 43 | each: function(list, callback) { 44 | return $A(list, function(item, index) { 45 | callback(index, item); 46 | }); 47 | }, 48 | 49 | map: function(callback) { 50 | return $A(value).map(callback); 51 | }, 52 | 53 | unique: function(array) { 54 | return $A(array).uniq(); 55 | }, 56 | 57 | merge: function(first, second) { 58 | return $A(first).merge(second); 59 | }, 60 | 61 | 62 | // the rest of the things 63 | 64 | extend: function() { 65 | return Object.merge.apply(Object, arguments); 66 | }, 67 | 68 | proxy: function(func, context) { 69 | return RightJS(func).bind(context); 70 | }, 71 | 72 | noop: function() { 73 | return RightJS(function() {}); 74 | }, 75 | 76 | noConflict: function( deep ) { 77 | if ( window.$ === jQuery ) { 78 | window.$ = _$; 79 | } 80 | 81 | if ( deep && window.jQuery === jQuery ) { 82 | window.jQuery = _jQuery; 83 | } 84 | 85 | return $; 86 | } 87 | 88 | }); -------------------------------------------------------------------------------- /src/jquerysh/jquerysh.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The plugin's definition 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | RightJS.jQuerysh = { 7 | version: '2.2.1', 8 | 9 | // collection methods 10 | collectionMethods: { 11 | live: function(event, callback) { 12 | this.cssRule.on(event, callback); 13 | return this; 14 | }, 15 | 16 | die: function(event, callback) { 17 | this.cssRule.stopObserving(event, callback); 18 | return this; 19 | } 20 | } 21 | }; -------------------------------------------------------------------------------- /src/json/__init__.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Initialization script 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | RightJS.JSON = window.JSON || {}; 7 | 8 | RightJS.JSON.version = '2.2.1'; 9 | 10 | include_module_files( 11 | 'json', 12 | 'cookie' 13 | ); -------------------------------------------------------------------------------- /src/json/cookie.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wraps up the Cooke set/get methods so that the values 3 | * were automatically exported/imported into JSON strings 4 | * and it allowed transparent objects and arrays saving 5 | * 6 | * @copyright (C) 2009-2010 Nikolay V. Nemshilov 7 | */ 8 | 9 | if (RightJS.Cookie) { 10 | var old_set = RightJS.Cookie.prototype.set, 11 | old_get = RightJS.Cookie.prototype.get; 12 | 13 | RightJS.Cookie.include({ 14 | set: function(value) { 15 | return old_set.call(this, JSON.stringify(value)); 16 | }, 17 | 18 | get: function() { 19 | return JSON.parse(old_get.call(this) || 'null'); 20 | } 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /src/json/json.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The generic JSON interface 3 | * 4 | * Credits: 5 | * Based on the original JSON escaping implementation 6 | * http://www.json.org/json2.js 7 | * 8 | * @copyright (C) 2009-2011 Nikolay V. Nemshilov 9 | */ 10 | var 11 | 12 | JSON = RightJS.JSON, 13 | 14 | // see the original JSON decoder implementation for descriptions http://www.json.org/json2.js 15 | cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 16 | specials = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'}, 17 | quotables = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 18 | 19 | 20 | // quotes the string 21 | function quote(string) { 22 | return string.replace(quotables, function(chr) { 23 | return specials[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4); 24 | }); 25 | } 26 | 27 | // adds the leading zero symbol 28 | function zerofy(num) { 29 | return (num < 10 ? '0' : '')+num; 30 | } 31 | 32 | /** 33 | * Checking if there is the JSON to String method 34 | */ 35 | if (!('stringify' in JSON)) { 36 | JSON.stringify = function(value) { 37 | if (value === null) { 38 | return 'null'; 39 | } else if (value.toJSON) { 40 | return value.toJSON(); 41 | } else { 42 | 43 | switch(typeof(value)) { 44 | case 'boolean': return String(value); 45 | case 'number': return String(value+0); 46 | case 'string': return '"'+ quote(value) + '"'; 47 | case 'object': 48 | 49 | if (value instanceof Array) { 50 | return '['+value.map(JSON.stringify).join(',')+']'; 51 | 52 | } else if (value instanceof Date) { 53 | return '"' + value.getUTCFullYear() + '-' + 54 | zerofy(value.getUTCMonth() + 1) + '-' + 55 | zerofy(value.getUTCDate()) + 'T' + 56 | zerofy(value.getUTCHours()) + ':' + 57 | zerofy(value.getUTCMinutes()) + ':' + 58 | zerofy(value.getUTCSeconds()) + '.' + 59 | zerofy(value.getMilliseconds()) + 'Z' + 60 | '"'; 61 | 62 | } else { 63 | var result = []; 64 | for (var key in value) { 65 | result.push(JSON.encode(key)+":"+JSON.encode(value[key])); 66 | } 67 | return '{'+result.join(',')+'}'; 68 | } 69 | } 70 | } 71 | }; 72 | } 73 | 74 | /** 75 | * Checking if there is the string to JSON method 76 | */ 77 | if (!('parse' in JSON)) { 78 | JSON.parse = function(string) { 79 | if (isString(string) && string) { 80 | // getting back the UTF-8 symbols 81 | string = string.replace(cx, function (a) { 82 | return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 83 | }); 84 | 85 | // checking the JSON string consistency 86 | if (/^[\],:{}\s]*$/.test(string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') 87 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') 88 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 89 | return new Function('return '+string)(); 90 | } 91 | } 92 | 93 | throw "JSON parse error: "+string; 94 | }; 95 | } 96 | 97 | RightJS.$alias(JSON, { 98 | encode: 'stringify', 99 | decode: 'parse' 100 | }); 101 | -------------------------------------------------------------------------------- /src/keys/__init__.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The keys plugin initialization 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | RightJS.Keys = { 7 | version: '2.2.1' 8 | }; 9 | 10 | include_module_files( 11 | 'event', 12 | 'element' 13 | ); -------------------------------------------------------------------------------- /src/keys/element.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Overloading the Element#on method to handle those fancy names 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | [RightJS.Document, RightJS.Element, RightJS.Window].each('include', { 7 | on: function() { 8 | var args = RightJS.$A(arguments), name = args[0]; 9 | 10 | if (typeof(name) === 'string') { 11 | var key = name.split(/[\+\-\_ ]+/); 12 | key = (key[key.length - 1] || '').toUpperCase(); 13 | 14 | if (key in RightJS.Event.Keys || /^[A-Z0-9]$/.test(key)) { 15 | var meta = /(^|\+|\-| )(meta|alt)(\+|\-| )/i.test(name), 16 | ctrl = /(^|\+|\-| )(ctl|ctrl)(\+|\-| )/i.test(name), 17 | shift = /(^|\+|\-| )(shift)(\+|\-| )/i.test(name), 18 | code = RightJS.Event.Keys[key] || key.charCodeAt(0), 19 | orig = args.slice(1), 20 | method = orig.shift(); 21 | 22 | if (typeof(method) === 'string') { 23 | method = this[method] || function() {}; 24 | } 25 | 26 | // replacing the arguments 27 | args = ['keydown', function(event) { 28 | var raw = event._; 29 | if ( 30 | (raw.keyCode === code) && 31 | (!meta || raw.metaKey || raw.altKey) && 32 | (!ctrl || raw.ctrlKey) && 33 | (!shift || raw.shiftKey) 34 | ) { 35 | return method.call(this, [event].concat(orig)); 36 | } 37 | }]; 38 | } 39 | } 40 | 41 | return this.$super.apply(this, args); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /src/keys/event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Event level keyboard keycodes and stuff 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | RightJS.Event.Keys = { 7 | BACKSPACE: 8, 8 | TAB: 9, 9 | ENTER: 13, 10 | ESC: 27, 11 | SPACE: 32, 12 | PAGEUP: 33, 13 | PAGEDOWN: 34, 14 | END: 35, 15 | HOME: 36, 16 | LEFT: 37, 17 | UP: 38, 18 | RIGHT: 39, 19 | DOWN: 40, 20 | INSERT: 45, 21 | DELETE: 46 22 | }; 23 | -------------------------------------------------------------------------------- /src/lang/hash.js: -------------------------------------------------------------------------------- 1 | /** 2 | * presents the Hash unit 3 | * 4 | * Copyright (C) 2008-2009 Nikolay V. Nemshilov 5 | */ 6 | var Hash = new Class({ 7 | /** 8 | * constructor 9 | * 10 | * @params Object optional initial object (will be cloned) 11 | */ 12 | initialize: function(object) { 13 | this.$ = {}; 14 | this.merge(object); 15 | }, 16 | 17 | $: null, // keeps the values which intersective with the Hash.prototype 18 | 19 | /** 20 | * checks if the hash has a value associated to the key 21 | * 22 | * @param String key 23 | */ 24 | has: function(key) { 25 | return (this.prototype[key] ? this.$ : this).hasOwnProperty(key); 26 | }, 27 | 28 | /** 29 | * Sets the value 30 | * 31 | * @param String key 32 | * @param mixed value 33 | * @return Hash self 34 | */ 35 | set: function(key, value) { 36 | (this.prototype[key] ? this.$ : this)[key] = value; 37 | return this; 38 | }, 39 | 40 | /** 41 | * retrieves a value by the key 42 | * 43 | * @param String key 44 | * @return mixed value or null if not set 45 | */ 46 | get: function(key) { 47 | return (this.prototype[key] ? this.$ : this)[key]; 48 | }, 49 | 50 | /** 51 | * removes a value associated with the key 52 | * 53 | * @param String key 54 | * ....... 55 | * @return Hash self 56 | */ 57 | remove: function(callback, scope) { 58 | var filter = $A(arguments), callback = isFunction(callback) ? callback : null, scope = scope || this; 59 | 60 | return this.each(function(key, value) { 61 | if (callback ? callback.apply(scope, [key, value, this]) : filter.includes(key)) 62 | delete((this.prototype[key] ? this.$ : this)[key]); 63 | }); 64 | }, 65 | 66 | /** 67 | * extracts the list of the attribute names of the given object 68 | * 69 | * @return Array keys list 70 | */ 71 | keys: function() { 72 | return this.map(function(key, value) { return key }); 73 | }, 74 | 75 | /** 76 | * extracts the list of the attribute values of the hash 77 | * 78 | * @return Array values list 79 | */ 80 | values: function() { 81 | return this.map(function(key, value) { return value }); 82 | }, 83 | 84 | /** 85 | * checks if the object-hash has no keys 86 | * 87 | * @return boolean check result 88 | */ 89 | empty: function() { 90 | var result = true; 91 | 92 | this.each(function() { result = false; $break(); }); 93 | 94 | return result; 95 | }, 96 | 97 | /** 98 | * iterates the given callback throwgh all the hash key -> value pairs 99 | * 100 | * @param Function callback 101 | * @param Object optional scope 102 | * @return Hash self 103 | */ 104 | each: function(callback, scope) { 105 | try { 106 | scope = scope || this; 107 | for (var key in this) 108 | if (!this.prototype[key]) 109 | callback.apply(scope, [key, this[key], this]); 110 | 111 | for (var key in this.$) 112 | callback.apply(scope, [key, this.$[key], this]); 113 | } catch(e) { if (!(e instanceof Break)) throw(e); } 114 | 115 | return this; 116 | }, 117 | 118 | /** 119 | * returns the list of results of calling the callback 120 | * 121 | * @param Function callback 122 | * @param Object scope 123 | * @return Array result 124 | */ 125 | map: function(callback, scope) { 126 | var result = [], scope = scope || this; 127 | 128 | this.each(function(key, value) { 129 | result.push(callback.apply(scope, [key, value, this])); 130 | }); 131 | 132 | return result; 133 | }, 134 | 135 | /** 136 | * creates a copy of the Hash keeping only the key-value pairs 137 | * which passes check in the callback 138 | * 139 | * @param Function callback 140 | * @param Object optional scope 141 | * @return Hash new 142 | */ 143 | filter: function(callback, scope) { 144 | var result = new Hash, scope = scope || this; 145 | 146 | this.each(function(key, value) { 147 | if (callback.apply(scope, [key, value, this])) 148 | result.set(key, value); 149 | }); 150 | 151 | return result; 152 | }, 153 | 154 | /** 155 | * removes all the items from the hashe where the callback returns true 156 | * 157 | * @param Function callback 158 | * @param Object optional scope 159 | * @return Hash self 160 | */ 161 | reject: function(callback, scope) { 162 | var result = new Hash, scope = scope || this; 163 | 164 | this.each(function(key, value) { 165 | if (!callback.apply(scope, [key, value, this])) 166 | result.set(key, value); 167 | }); 168 | 169 | return result; 170 | }, 171 | 172 | /** 173 | * merges the given objects into the current hash 174 | * 175 | * @param Object object 176 | * ...... 177 | * @return Hash all 178 | */ 179 | merge: function() { 180 | var args = arguments; 181 | for (var i=0; i < args.length; i++) { 182 | if (isHash(args[i])) { 183 | if (args[i] instanceof Hash) { 184 | args[i].each(function(key, value) { 185 | this.set(key, value); 186 | }, this); 187 | } else { 188 | for (var key in args[i]) 189 | this.set(key, args[i][key]); 190 | } 191 | } 192 | } 193 | return this; 194 | }, 195 | 196 | /** 197 | * converts the hash into a usual object hash 198 | * 199 | * @return Object 200 | */ 201 | toObject: function() { 202 | var object = {}; 203 | this.each(function(key, value) { object[key] = value; }); 204 | return object; 205 | }, 206 | 207 | /** 208 | * converts a hash-object into an equivalent url query string 209 | * 210 | * @return String query 211 | */ 212 | toQueryString: function() { 213 | return Object.toQueryString(this.toObject()); 214 | } 215 | }); -------------------------------------------------------------------------------- /src/lang/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Additional language level units for RightJS 3 | * 4 | * Copyright (C) 2008-2010 Nikolay V. Nemshilov 5 | */ 6 | -------------------------------------------------------------------------------- /src/lang/num_range.js: -------------------------------------------------------------------------------- 1 | /** 2 | * presents the Range unit 3 | * 4 | * Copyright (C) 2008-2009 Nikolay V. Nemshilov 5 | */ 6 | var NumRange = new Class({ 7 | /** 8 | * basic constructor 9 | * 10 | * @param Number start 11 | * @param Number end 12 | */ 13 | initialize: function(start, end, step) { 14 | this.start = start; 15 | this.end = end; 16 | this.step = (step || 1).abs() * ( start > end ? 1 : -1); 17 | }, 18 | 19 | each: function(callback, scope) { 20 | var scope = scope || this; 21 | for (var value=this.start, i=0; value < this.end; value += this.step) { 22 | callback.call(scope, value, i++, this); 23 | } 24 | }, 25 | 26 | map: function(callback, scope) { 27 | var result = [], scope = scope || this; 28 | 29 | this.each(function(value, i) { 30 | result.push(callback.call(scope, value, i, this)); 31 | }); 32 | 33 | return result; 34 | }, 35 | 36 | filter: function(callback, scope) { 37 | var result = [], scope = scope || this; 38 | 39 | this.each(function(value, i) { 40 | if (callback.call(scope, value, i, this)) { 41 | result.push(value); 42 | } 43 | }); 44 | 45 | return result; 46 | }, 47 | 48 | reject: function(callback, scope) { 49 | var result = [], scope = scope || this; 50 | 51 | this.each(function(value, i) { 52 | if (!callback.call(scope, value, i, this)) { 53 | result.push(value); 54 | } 55 | }); 56 | 57 | return result; 58 | } 59 | }); -------------------------------------------------------------------------------- /src/lang/shortcuts.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Additional language unit shortcuts 3 | * 4 | * Copyright (C) 2009-2010 Nikolay V. Nemshilov 5 | */ 6 | function $H(object) { 7 | return new Hash(object); 8 | }; 9 | 10 | function $R(start, end, step) { 11 | return new NumRange(start, end, step); 12 | }; -------------------------------------------------------------------------------- /src/lang/string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The String unit additionals 3 | * 4 | * Credits: 5 | * The faster trim method is based on the work of Yesudeep Mangalapilly 6 | * http://yesudeep.wordpress.com/2009/07/31/even-faster-string-prototype-trim-implementation-in-javascript/ 7 | * 8 | * @copyright (C) 2009-2010 Nikolay V. Nemshilov 9 | */ 10 | 11 | if (String.prototype.trim.toString().include('return')) { 12 | String.WSPS = []; 13 | $w("0009 000a 000b 000c 000d 0020 0085 00a0 1680 180e 2000 2001 2002 2003 2004 2005 "+ 14 | "2006 2007 2008 2009 200a 200b 2028 2029 202f 205f 3000").each(function(key) { 15 | String.WSPS[key.toInt(16)] = 1; 16 | }); 17 | 18 | String.include({ 19 | trim: function() { 20 | var str = this, len = this.length, i = 0; 21 | if (len) { 22 | while (String.WSPS[str.charCodeAt(--len)]); 23 | if (++len) { 24 | while(String.WSPS[str.charCodeAt(i)]) i++; 25 | } 26 | str = str.substring(i, len); 27 | } 28 | return str; 29 | } 30 | }); 31 | } 32 | 33 | String.include({ 34 | truncate: function(length, after) { 35 | var after = defined(after) ? after : '...'; 36 | 37 | return this.length <= length ? this : 38 | this.substr(0, length - after.length) + after; 39 | } 40 | }); -------------------------------------------------------------------------------- /src/rails/__init__.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Rails plugin initialization script 3 | * 4 | * Copyright (C) 2010-2012 Nikolay Nemshilov 5 | */ 6 | 7 | var R = RightJS, 8 | $ = RightJS.$, 9 | $$ = RightJS.$$, 10 | $E = RightJS.$E, 11 | Xhr = RightJS.Xhr, 12 | Object = RightJS.Object, 13 | Input = RightJS.Input; 14 | 15 | RightJS.Rails = { 16 | version: '2.3.2' 17 | }; 18 | 19 | include_module_files( 20 | 'aliases', 21 | 'ujs', 22 | 'rr', 23 | 'document' 24 | ); -------------------------------------------------------------------------------- /src/rails/aliases.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Underscored aliases for Ruby On Rails 3 | * 4 | * Copyright (C) 2009-2011 Nikolay Nemshilov 5 | */ 6 | 7 | // the language and window level aliases 8 | R([ 9 | RightJS.String.prototype, 10 | RightJS.Array.prototype, 11 | RightJS.Function.prototype, 12 | RightJS.Object, 13 | RightJS.Options, 14 | RightJS.Observer, 15 | RightJS.Observer.prototype, 16 | RightJS.Window.prototype, 17 | RightJS.Document.prototype 18 | ]).each(function(object) { 19 | for (var key in object) { 20 | try { // some keys are not accessable 21 | 22 | if (/[A-Z]/.test(key) && typeof(object[key]) === 'function') { 23 | var u_key = R(key).underscored(); 24 | if (object[u_key] === null || object[u_key] === undefined) { 25 | object[u_key] = object[key]; 26 | } 27 | } 28 | } catch (e) {} 29 | } 30 | }); 31 | 32 | 33 | // DOM package aliases 34 | R([ 35 | RightJS.Element, 36 | RightJS.Event, 37 | RightJS.Form, 38 | RightJS.Input 39 | ]).each(function(object) { 40 | if (!object) { return; } 41 | 42 | var aliases = {}, methods = object.prototype; 43 | 44 | for (var key in methods) { 45 | if (/[A-Z]/.test(key) && typeof(methods[key]) === 'function') { 46 | object.prototype[R(key).underscored()] = methods[key]; 47 | } 48 | } 49 | }); 50 | 51 | // various ruby-like method aliases 52 | RightJS.$alias(RightJS.String.prototype, { 53 | index_of: 'indexOf', 54 | last_index_of: 'lastIndexOf', 55 | to_f: 'toFloat', 56 | to_i: 'toInt', 57 | gsub: 'replace', 58 | downcase: 'toLowerCase', 59 | upcase: 'toUpperCase', 60 | index: 'indexOf', 61 | rindex: 'lastIndexOf', 62 | strip: 'trim' 63 | }); 64 | 65 | RightJS.$alias(RightJS.Array.prototype, { 66 | collect: 'map', 67 | detect: 'filter', 68 | index_of: 'indexOf', 69 | last_index_of: 'lastIndexOf', 70 | index: 'indexOf', 71 | rindex: 'lastIndexOf' 72 | }); -------------------------------------------------------------------------------- /src/rails/document.js: -------------------------------------------------------------------------------- 1 | /** 2 | * the document onload hooks 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | $(document).on({ 7 | ready: function() { 8 | RR.hide_flash(); 9 | }, 10 | 11 | click: function(event) { 12 | RR.process_click(event); 13 | } 14 | }); -------------------------------------------------------------------------------- /src/rails/rr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RR is the common ajax operations wrapper for ruby on rails 3 | * 4 | * Copyright (C) 2009-2010 Nikolay Nemshilov 5 | */ 6 | var RR = { 7 | /** 8 | * Basic options 9 | * 10 | * NOTE: DO NOT CHANGE this hash right here 11 | * Use your application.js file to alter the options 12 | */ 13 | Options: { 14 | format: 'js', // the working format for remote requests over the application 15 | 16 | flashId: 'flashes', // the flashes element id 17 | flashHideFx: 'slide', // use null if you don't want any fx in here 18 | flashHideDelay: 3200, // use -1 to disable the flash element hidding 19 | 20 | highlightUpdates: true, 21 | 22 | removeFx: 'fade', // blocks removing fx 23 | insertFx: 'fade', // blocks insertion fx 24 | 25 | insertPosition: 'bottom', // default insert position 26 | 27 | linkToAjaxEdit: '.ajax_edit', 28 | linkToAjaxDelete: '.ajax_delete', 29 | 30 | rescanWithScopes: true // if it should rescan only updated elements 31 | }, 32 | 33 | /** 34 | * Updates the flashes block with the source 35 | * 36 | * @param String new content 37 | * @return RR this 38 | */ 39 | update_flash: function(content) { 40 | var element = $(this.Options.flashId); 41 | if (element) { 42 | this.replace(element, content).hide_flash(); 43 | } 44 | return this; 45 | }, 46 | 47 | /** 48 | * Initializes the delayed flashes hide call 49 | * 50 | * @return RR this 51 | */ 52 | hide_flash: function() { 53 | if (this.Options.flashHideDelay > -1) { 54 | var element = $(this.Options.flashId); 55 | if (element && element.visible()) { 56 | element.hide.bind(element, this.Options.flashHideFx).delay(this.Options.flashHideDelay); 57 | } 58 | } 59 | return this; 60 | }, 61 | 62 | /** 63 | * Highlights the element according to the options 64 | * 65 | * @param String element id 66 | * @return RR this 67 | */ 68 | highlight: function(id) { 69 | if ($(id) && this.Options.highlightUpdates) { 70 | $(id).highlight(); 71 | } 72 | return this; 73 | }, 74 | 75 | /** 76 | * Inserts the content into the given element 77 | * 78 | * @param destination String destination id 79 | * @param content String content 80 | * @param position String position 81 | * @return RR this 82 | */ 83 | insert: function(where, what, in_position) { 84 | var position = in_position || this.Options.insertPosition, new_element, 85 | container = $(where).insert(what, position); 86 | 87 | // trying to find the new block 88 | switch (position) { 89 | case 'bottom': new_element = container.children().last(); break; 90 | case 'top': new_element = container.first(); break; 91 | case 'before': new_element = container.prev(); break; 92 | case 'after': new_element = container.next(); break; 93 | } 94 | 95 | // necely displaying the new block 96 | if (new_element && this.Options.insertFx) { 97 | new_element.hide().show(this.Options.insertFx, { 98 | onFinish: this.highlight.bind(this, new_element) 99 | }); 100 | } else { 101 | this.highlight(new_element); 102 | } 103 | 104 | return this.rescan(where); 105 | }, 106 | 107 | /** 108 | * Replaces the given element with a new content 109 | * 110 | * @param destination String destination id 111 | * @param content String content 112 | * @return RR this 113 | */ 114 | replace: function(id, source) { 115 | $(id).replace(source); 116 | return this.highlight(id).rescan(id); 117 | }, 118 | 119 | /** 120 | * removes the element by id 121 | * 122 | * @param String element id 123 | * @return RR this 124 | */ 125 | remove: function(id) { 126 | if ($(id)) { 127 | $(id).remove(this.Options.removeFx); 128 | } 129 | }, 130 | 131 | /** 132 | * Makes a remote form out of the form 133 | * 134 | * @param String form id 135 | * @return RR this 136 | */ 137 | remotize_form: function(id) { 138 | var form = $(id); 139 | if (form) { 140 | form.remotize().enable()._.action += '.'+this.Options.format; 141 | } 142 | return this; 143 | }, 144 | 145 | /** 146 | * Replaces the form with new content and makes it remote 147 | * 148 | * @param form id String form id 149 | * @param content String content 150 | * @return RR this 151 | */ 152 | replace_form: function(id, source) { 153 | var form = $(id); 154 | if (form) { 155 | form.replace(source); 156 | this.remotize_form(id); 157 | } 158 | 159 | return this.rescan(id); 160 | }, 161 | 162 | /** 163 | * Inserts the form source into the given element 164 | * 165 | * @param target id String target id 166 | * @param source String form source 167 | * @return RR this 168 | */ 169 | show_form_for: function(id, source) { 170 | $(id).find('form').each('remove'); // removing old forms 171 | $(id).insert(source); 172 | 173 | return this.remotize_form($(id).first('form')).rescan(id); 174 | }, 175 | 176 | /** 177 | * watches link clicks and processes the ajax edit/delete operations 178 | * 179 | * @param Event event 180 | */ 181 | process_click: function(event) { 182 | var link; 183 | 184 | if ((link = event.find('a'+ this.Options.linkToAjaxEdit))) { 185 | event.stop(); 186 | Xhr.load(link.get('href') + '.' + this.Options.format); 187 | } else if ((link = event.find('a'+ this.Options.linkToAjaxDelete)) && link.has('onclick')) { 188 | event.stop(); 189 | new Function('return '+ link.onclick.toString().replace('.submit', '.send'))().call(link); 190 | } 191 | }, 192 | 193 | /** 194 | * Scans for updated elements 195 | * 196 | * @return RR this 197 | */ 198 | rescan: function(scope) { 199 | $w('Draggable Droppable Tabs Tags Selectable').each(function(name) { 200 | if (name in window) { 201 | window[name].rescan(this.Options.rescanWithScopes ? scope : null); 202 | } 203 | }, this); 204 | 205 | 206 | return this; 207 | } 208 | }; 209 | -------------------------------------------------------------------------------- /src/rails/ujs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Rails 3 UJS support module 3 | * 4 | * Copyright (C) 2010-2012 Nikolay Nemshilov 5 | */ 6 | // tries to cancel the event via confirmation 7 | function user_cancels(event, element) { 8 | var message = element.get('data-confirm'); 9 | if (message && !confirm(message)) { 10 | event.stop(); 11 | return true; 12 | } 13 | } 14 | 15 | // adds XHR events to the element 16 | function add_xhr_events(element, options) { 17 | return Object.merge({ 18 | onCreate: function() { 19 | disable_with(element); 20 | element.fire('ajax:loading', {xhr: this}); 21 | }, 22 | onComplete: function() { 23 | enable_with(element); 24 | element.fire('ajax:complete', {xhr: this}); 25 | }, 26 | onSuccess: function() { element.fire('ajax:success', {xhr: this}); }, 27 | onFailure: function() { element.fire('ajax:failure', {xhr: this}); } 28 | }, options); 29 | } 30 | 31 | // handles the data-disable-with option 32 | function disable_with(element) { 33 | get_disable_with_elements(element).each(function(element) { 34 | var method = element instanceof Input ? 'value' : 'html'; 35 | element.__disable_with_html = element[method](); 36 | element[method](element.get('data-disable-with')); 37 | }); 38 | } 39 | 40 | // restores the elements state after the data-disable-with option 41 | function enable_with(element) { 42 | get_disable_with_elements(element).each(function(element) { 43 | if (element.__disable_with_html !== undefined) { 44 | var method = element instanceof Input ? 'value' : 'html'; 45 | element[method](element.__disable_with_html); 46 | delete(element.__disable_with_html); 47 | } 48 | }); 49 | } 50 | 51 | // finds all the suitable disable-with targets 52 | function get_disable_with_elements(element) { 53 | return element.has('data-disable-with') ? 54 | R([element]) : element.find('*[data-disable-with]'); 55 | } 56 | 57 | // processes link clicks 58 | function try_link_submit(event, link) { 59 | var url = link.get('href'), 60 | method = link.get('data-method'), 61 | remote = link.get('data-remote'), 62 | token = get_csrf_token(); 63 | 64 | if (user_cancels(event, link)) { return; } 65 | if (method || remote) { event.stop(); } 66 | 67 | if (remote) { 68 | Xhr.load(url, add_xhr_events(link, { 69 | method: method || 'get', 70 | spinner: link.get('data-spinner'), 71 | params: new Function('return {"'+ token[0] +'": "'+ token[1] +'"}')() 72 | })); 73 | 74 | } else if (method) { 75 | var form = $E('form', {action: url, method: 'post'}); 76 | 77 | if (token) { 78 | form.insert(''); 79 | } 80 | 81 | form.insert('') 82 | .insertTo(document.body).submit(); 83 | 84 | disable_with(link); 85 | } 86 | } 87 | 88 | function get_csrf_token() { 89 | var param, token; 90 | 91 | param = $$('meta[name=csrf-param]')[0]; 92 | token = $$('meta[name=csrf-token]')[0]; 93 | 94 | param = param && param.get('content'); 95 | token = token && token.get('content'); 96 | 97 | if (param && token) { 98 | return [param, token]; 99 | } 100 | } 101 | 102 | // global events listeners 103 | $(document).on({ 104 | ready: function() { 105 | var token = get_csrf_token(), i = 0, xhr, 106 | modules = ['InEdit', 'Rater', 'Sortable']; 107 | 108 | if (token) { 109 | for (; i < modules.length; i++) { 110 | if (modules[i] in RightJS) { 111 | xhr = RightJS[modules[i]].Options.Xhr; 112 | 113 | if (RightJS.isHash(xhr)) { 114 | xhr.params = Object.merge(xhr.params, {}); 115 | xhr.params[token[0]] = token[1]; 116 | } 117 | } 118 | } 119 | } 120 | }, 121 | 122 | click: function(event) { 123 | var link = event.find('a'); 124 | if (link) { 125 | try_link_submit(event, link); 126 | } 127 | }, 128 | 129 | submit: function(event) { 130 | var form = event.target; 131 | if (form.has('data-remote') && !user_cancels(event, form)) { 132 | event.stop(); 133 | form.send(add_xhr_events(form, { 134 | spinner: form.get('data-spinner') || form.first('.spinner') 135 | })); 136 | } 137 | } 138 | }); -------------------------------------------------------------------------------- /src/sizzle/__init__.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sizzle initialization script 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | 7 | RightJS.Sizzle = { 8 | version: '2.2.0' 9 | }; 10 | 11 | include_module_files( 12 | 'sizzle', 13 | 'hooks' 14 | ); 15 | -------------------------------------------------------------------------------- /src/sizzle/hooks.js: -------------------------------------------------------------------------------- 1 | RightJS([RightJS.Document, RightJS.Element]).each('include', { 2 | first: function(rule) { 3 | return this.find(rule)[0]; 4 | }, 5 | 6 | find: function(rule) { 7 | return RightJS(Sizzle(rule, this._)).map(RightJS.$); 8 | } 9 | }); -------------------------------------------------------------------------------- /src/sizzle/sizzle.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Sizzle CSS Selector Engine - v1.0 3 | * Copyright 2009, The Dojo Foundation 4 | * Released under the MIT, BSD, and GPL Licenses. 5 | * More information: http://sizzlejs.com/ 6 | */ 7 | (function(){ 8 | 9 | var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, 10 | done = 0, 11 | toString = Object.prototype.toString, 12 | hasDuplicate = false, 13 | baseHasDuplicate = true; 14 | 15 | // Here we check if the JavaScript engine is using some sort of 16 | // optimization where it does not always call our comparision 17 | // function. If that is the case, discard the hasDuplicate value. 18 | // Thus far that includes Google Chrome. 19 | [0, 0].sort(function(){ 20 | baseHasDuplicate = false; 21 | return 0; 22 | }); 23 | 24 | var Sizzle = function(selector, context, results, seed) { 25 | results = results || []; 26 | context = context || document; 27 | 28 | var origContext = context; 29 | 30 | if ( context.nodeType !== 1 && context.nodeType !== 9 ) { 31 | return []; 32 | } 33 | 34 | if ( !selector || typeof selector !== "string" ) { 35 | return results; 36 | } 37 | 38 | var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context), 39 | soFar = selector, ret, cur, pop, i; 40 | 41 | // Reset the position of the chunker regexp (start from head) 42 | do { 43 | chunker.exec(""); 44 | m = chunker.exec(soFar); 45 | 46 | if ( m ) { 47 | soFar = m[3]; 48 | 49 | parts.push( m[1] ); 50 | 51 | if ( m[2] ) { 52 | extra = m[3]; 53 | break; 54 | } 55 | } 56 | } while ( m ); 57 | 58 | if ( parts.length > 1 && origPOS.exec( selector ) ) { 59 | if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { 60 | set = posProcess( parts[0] + parts[1], context ); 61 | } else { 62 | set = Expr.relative[ parts[0] ] ? 63 | [ context ] : 64 | Sizzle( parts.shift(), context ); 65 | 66 | while ( parts.length ) { 67 | selector = parts.shift(); 68 | 69 | if ( Expr.relative[ selector ] ) { 70 | selector += parts.shift(); 71 | } 72 | 73 | set = posProcess( selector, set ); 74 | } 75 | } 76 | } else { 77 | // Take a shortcut and set the context if the root selector is an ID 78 | // (but not if it'll be faster if the inner selector is an ID) 79 | if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && 80 | Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { 81 | ret = Sizzle.find( parts.shift(), context, contextXML ); 82 | context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; 83 | } 84 | 85 | if ( context ) { 86 | ret = seed ? 87 | { expr: parts.pop(), set: makeArray(seed) } : 88 | Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); 89 | set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; 90 | 91 | if ( parts.length > 0 ) { 92 | checkSet = makeArray(set); 93 | } else { 94 | prune = false; 95 | } 96 | 97 | while ( parts.length ) { 98 | cur = parts.pop(); 99 | pop = cur; 100 | 101 | if ( !Expr.relative[ cur ] ) { 102 | cur = ""; 103 | } else { 104 | pop = parts.pop(); 105 | } 106 | 107 | if ( pop == null ) { 108 | pop = context; 109 | } 110 | 111 | Expr.relative[ cur ]( checkSet, pop, contextXML ); 112 | } 113 | } else { 114 | checkSet = parts = []; 115 | } 116 | } 117 | 118 | if ( !checkSet ) { 119 | checkSet = set; 120 | } 121 | 122 | if ( !checkSet ) { 123 | Sizzle.error( cur || selector ); 124 | } 125 | 126 | if ( toString.call(checkSet) === "[object Array]" ) { 127 | if ( !prune ) { 128 | results.push.apply( results, checkSet ); 129 | } else if ( context && context.nodeType === 1 ) { 130 | for ( i = 0; checkSet[i] != null; i++ ) { 131 | if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { 132 | results.push( set[i] ); 133 | } 134 | } 135 | } else { 136 | for ( i = 0; checkSet[i] != null; i++ ) { 137 | if ( checkSet[i] && checkSet[i].nodeType === 1 ) { 138 | results.push( set[i] ); 139 | } 140 | } 141 | } 142 | } else { 143 | makeArray( checkSet, results ); 144 | } 145 | 146 | if ( extra ) { 147 | Sizzle( extra, origContext, results, seed ); 148 | Sizzle.uniqueSort( results ); 149 | } 150 | 151 | return results; 152 | }; 153 | 154 | Sizzle.uniqueSort = function(results){ 155 | if ( sortOrder ) { 156 | hasDuplicate = baseHasDuplicate; 157 | results.sort(sortOrder); 158 | 159 | if ( hasDuplicate ) { 160 | for ( var i = 1; i < results.length; i++ ) { 161 | if ( results[i] === results[i-1] ) { 162 | results.splice(i--, 1); 163 | } 164 | } 165 | } 166 | } 167 | 168 | return results; 169 | }; 170 | 171 | Sizzle.matches = function(expr, set){ 172 | return Sizzle(expr, null, null, set); 173 | }; 174 | 175 | Sizzle.find = function(expr, context, isXML){ 176 | var set; 177 | 178 | if ( !expr ) { 179 | return []; 180 | } 181 | 182 | for ( var i = 0, l = Expr.order.length; i < l; i++ ) { 183 | var type = Expr.order[i], match; 184 | 185 | if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { 186 | var left = match[1]; 187 | match.splice(1,1); 188 | 189 | if ( left.substr( left.length - 1 ) !== "\\" ) { 190 | match[1] = (match[1] || "").replace(/\\/g, ""); 191 | set = Expr.find[ type ]( match, context, isXML ); 192 | if ( set != null ) { 193 | expr = expr.replace( Expr.match[ type ], "" ); 194 | break; 195 | } 196 | } 197 | } 198 | } 199 | 200 | if ( !set ) { 201 | set = context.getElementsByTagName("*"); 202 | } 203 | 204 | return {set: set, expr: expr}; 205 | }; 206 | 207 | Sizzle.filter = function(expr, set, inplace, not){ 208 | var old = expr, result = [], curLoop = set, match, anyFound, 209 | isXMLFilter = set && set[0] && Sizzle.isXML(set[0]); 210 | 211 | while ( expr && set.length ) { 212 | for ( var type in Expr.filter ) { 213 | if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { 214 | var filter = Expr.filter[ type ], found, item, left = match[1]; 215 | anyFound = false; 216 | 217 | match.splice(1,1); 218 | 219 | if ( left.substr( left.length - 1 ) === "\\" ) { 220 | continue; 221 | } 222 | 223 | if ( curLoop === result ) { 224 | result = []; 225 | } 226 | 227 | if ( Expr.preFilter[ type ] ) { 228 | match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); 229 | 230 | if ( !match ) { 231 | anyFound = found = true; 232 | } else if ( match === true ) { 233 | continue; 234 | } 235 | } 236 | 237 | if ( match ) { 238 | for ( var i = 0; (item = curLoop[i]) != null; i++ ) { 239 | if ( item ) { 240 | found = filter( item, match, i, curLoop ); 241 | var pass = not ^ !!found; 242 | 243 | if ( inplace && found != null ) { 244 | if ( pass ) { 245 | anyFound = true; 246 | } else { 247 | curLoop[i] = false; 248 | } 249 | } else if ( pass ) { 250 | result.push( item ); 251 | anyFound = true; 252 | } 253 | } 254 | } 255 | } 256 | 257 | if ( found !== undefined ) { 258 | if ( !inplace ) { 259 | curLoop = result; 260 | } 261 | 262 | expr = expr.replace( Expr.match[ type ], "" ); 263 | 264 | if ( !anyFound ) { 265 | return []; 266 | } 267 | 268 | break; 269 | } 270 | } 271 | } 272 | 273 | // Improper expression 274 | if ( expr === old ) { 275 | if ( anyFound == null ) { 276 | Sizzle.error( expr ); 277 | } else { 278 | break; 279 | } 280 | } 281 | 282 | old = expr; 283 | } 284 | 285 | return curLoop; 286 | }; 287 | 288 | Sizzle.error = function( msg ) { 289 | throw "Syntax error, unrecognized expression: " + msg; 290 | }; 291 | 292 | var Expr = Sizzle.selectors = { 293 | order: [ "ID", "NAME", "TAG" ], 294 | match: { 295 | ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, 296 | CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, 297 | NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, 298 | ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, 299 | TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, 300 | CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/, 301 | POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, 302 | PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ 303 | }, 304 | leftMatch: {}, 305 | attrMap: { 306 | "class": "className", 307 | "for": "htmlFor" 308 | }, 309 | attrHandle: { 310 | href: function(elem){ 311 | return elem.getAttribute("href"); 312 | } 313 | }, 314 | relative: { 315 | "+": function(checkSet, part){ 316 | var isPartStr = typeof part === "string", 317 | isTag = isPartStr && !/\W/.test(part), 318 | isPartStrNotTag = isPartStr && !isTag; 319 | 320 | if ( isTag ) { 321 | part = part.toLowerCase(); 322 | } 323 | 324 | for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { 325 | if ( (elem = checkSet[i]) ) { 326 | while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} 327 | 328 | checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? 329 | elem || false : 330 | elem === part; 331 | } 332 | } 333 | 334 | if ( isPartStrNotTag ) { 335 | Sizzle.filter( part, checkSet, true ); 336 | } 337 | }, 338 | ">": function(checkSet, part){ 339 | var isPartStr = typeof part === "string", 340 | elem, i = 0, l = checkSet.length; 341 | 342 | if ( isPartStr && !/\W/.test(part) ) { 343 | part = part.toLowerCase(); 344 | 345 | for ( ; i < l; i++ ) { 346 | elem = checkSet[i]; 347 | if ( elem ) { 348 | var parent = elem.parentNode; 349 | checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; 350 | } 351 | } 352 | } else { 353 | for ( ; i < l; i++ ) { 354 | elem = checkSet[i]; 355 | if ( elem ) { 356 | checkSet[i] = isPartStr ? 357 | elem.parentNode : 358 | elem.parentNode === part; 359 | } 360 | } 361 | 362 | if ( isPartStr ) { 363 | Sizzle.filter( part, checkSet, true ); 364 | } 365 | } 366 | }, 367 | "": function(checkSet, part, isXML){ 368 | var doneName = done++, checkFn = dirCheck, nodeCheck; 369 | 370 | if ( typeof part === "string" && !/\W/.test(part) ) { 371 | part = part.toLowerCase(); 372 | nodeCheck = part; 373 | checkFn = dirNodeCheck; 374 | } 375 | 376 | checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); 377 | }, 378 | "~": function(checkSet, part, isXML){ 379 | var doneName = done++, checkFn = dirCheck, nodeCheck; 380 | 381 | if ( typeof part === "string" && !/\W/.test(part) ) { 382 | part = part.toLowerCase(); 383 | nodeCheck = part; 384 | checkFn = dirNodeCheck; 385 | } 386 | 387 | checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); 388 | } 389 | }, 390 | find: { 391 | ID: function(match, context, isXML){ 392 | if ( typeof context.getElementById !== "undefined" && !isXML ) { 393 | var m = context.getElementById(match[1]); 394 | // Check parentNode to catch when Blackberry 4.6 returns 395 | // nodes that are no longer in the document #6963 396 | return m && m.parentNode ? [m] : []; 397 | } 398 | }, 399 | NAME: function(match, context){ 400 | if ( typeof context.getElementsByName !== "undefined" ) { 401 | var ret = [], results = context.getElementsByName(match[1]); 402 | 403 | for ( var i = 0, l = results.length; i < l; i++ ) { 404 | if ( results[i].getAttribute("name") === match[1] ) { 405 | ret.push( results[i] ); 406 | } 407 | } 408 | 409 | return ret.length === 0 ? null : ret; 410 | } 411 | }, 412 | TAG: function(match, context){ 413 | return context.getElementsByTagName(match[1]); 414 | } 415 | }, 416 | preFilter: { 417 | CLASS: function(match, curLoop, inplace, result, not, isXML){ 418 | match = " " + match[1].replace(/\\/g, "") + " "; 419 | 420 | if ( isXML ) { 421 | return match; 422 | } 423 | 424 | for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { 425 | if ( elem ) { 426 | if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) { 427 | if ( !inplace ) { 428 | result.push( elem ); 429 | } 430 | } else if ( inplace ) { 431 | curLoop[i] = false; 432 | } 433 | } 434 | } 435 | 436 | return false; 437 | }, 438 | ID: function(match){ 439 | return match[1].replace(/\\/g, ""); 440 | }, 441 | TAG: function(match, curLoop){ 442 | return match[1].toLowerCase(); 443 | }, 444 | CHILD: function(match){ 445 | if ( match[1] === "nth" ) { 446 | // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' 447 | var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( 448 | match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || 449 | !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); 450 | 451 | // calculate the numbers (first)n+(last) including if they are negative 452 | match[2] = (test[1] + (test[2] || 1)) - 0; 453 | match[3] = test[3] - 0; 454 | } 455 | 456 | // TODO: Move to normal caching system 457 | match[0] = done++; 458 | 459 | return match; 460 | }, 461 | ATTR: function(match, curLoop, inplace, result, not, isXML){ 462 | var name = match[1].replace(/\\/g, ""); 463 | 464 | if ( !isXML && Expr.attrMap[name] ) { 465 | match[1] = Expr.attrMap[name]; 466 | } 467 | 468 | if ( match[2] === "~=" ) { 469 | match[4] = " " + match[4] + " "; 470 | } 471 | 472 | return match; 473 | }, 474 | PSEUDO: function(match, curLoop, inplace, result, not){ 475 | if ( match[1] === "not" ) { 476 | // If we're dealing with a complex expression, or a simple one 477 | if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { 478 | match[3] = Sizzle(match[3], null, null, curLoop); 479 | } else { 480 | var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); 481 | if ( !inplace ) { 482 | result.push.apply( result, ret ); 483 | } 484 | return false; 485 | } 486 | } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { 487 | return true; 488 | } 489 | 490 | return match; 491 | }, 492 | POS: function(match){ 493 | match.unshift( true ); 494 | return match; 495 | } 496 | }, 497 | filters: { 498 | enabled: function(elem){ 499 | return elem.disabled === false && elem.type !== "hidden"; 500 | }, 501 | disabled: function(elem){ 502 | return elem.disabled === true; 503 | }, 504 | checked: function(elem){ 505 | return elem.checked === true; 506 | }, 507 | selected: function(elem){ 508 | // Accessing this property makes selected-by-default 509 | // options in Safari work properly 510 | elem.parentNode.selectedIndex; 511 | return elem.selected === true; 512 | }, 513 | parent: function(elem){ 514 | return !!elem.firstChild; 515 | }, 516 | empty: function(elem){ 517 | return !elem.firstChild; 518 | }, 519 | has: function(elem, i, match){ 520 | return !!Sizzle( match[3], elem ).length; 521 | }, 522 | header: function(elem){ 523 | return (/h\d/i).test( elem.nodeName ); 524 | }, 525 | text: function(elem){ 526 | return "text" === elem.type; 527 | }, 528 | radio: function(elem){ 529 | return "radio" === elem.type; 530 | }, 531 | checkbox: function(elem){ 532 | return "checkbox" === elem.type; 533 | }, 534 | file: function(elem){ 535 | return "file" === elem.type; 536 | }, 537 | password: function(elem){ 538 | return "password" === elem.type; 539 | }, 540 | submit: function(elem){ 541 | return "submit" === elem.type; 542 | }, 543 | image: function(elem){ 544 | return "image" === elem.type; 545 | }, 546 | reset: function(elem){ 547 | return "reset" === elem.type; 548 | }, 549 | button: function(elem){ 550 | return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; 551 | }, 552 | input: function(elem){ 553 | return (/input|select|textarea|button/i).test(elem.nodeName); 554 | } 555 | }, 556 | setFilters: { 557 | first: function(elem, i){ 558 | return i === 0; 559 | }, 560 | last: function(elem, i, match, array){ 561 | return i === array.length - 1; 562 | }, 563 | even: function(elem, i){ 564 | return i % 2 === 0; 565 | }, 566 | odd: function(elem, i){ 567 | return i % 2 === 1; 568 | }, 569 | lt: function(elem, i, match){ 570 | return i < match[3] - 0; 571 | }, 572 | gt: function(elem, i, match){ 573 | return i > match[3] - 0; 574 | }, 575 | nth: function(elem, i, match){ 576 | return match[3] - 0 === i; 577 | }, 578 | eq: function(elem, i, match){ 579 | return match[3] - 0 === i; 580 | } 581 | }, 582 | filter: { 583 | PSEUDO: function(elem, match, i, array){ 584 | var name = match[1], filter = Expr.filters[ name ]; 585 | 586 | if ( filter ) { 587 | return filter( elem, i, match, array ); 588 | } else if ( name === "contains" ) { 589 | return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; 590 | } else if ( name === "not" ) { 591 | var not = match[3]; 592 | 593 | for ( var j = 0, l = not.length; j < l; j++ ) { 594 | if ( not[j] === elem ) { 595 | return false; 596 | } 597 | } 598 | 599 | return true; 600 | } else { 601 | Sizzle.error( "Syntax error, unrecognized expression: " + name ); 602 | } 603 | }, 604 | CHILD: function(elem, match){ 605 | var type = match[1], node = elem; 606 | switch (type) { 607 | case 'only': 608 | case 'first': 609 | while ( (node = node.previousSibling) ) { 610 | if ( node.nodeType === 1 ) { 611 | return false; 612 | } 613 | } 614 | if ( type === "first" ) { 615 | return true; 616 | } 617 | node = elem; 618 | case 'last': 619 | while ( (node = node.nextSibling) ) { 620 | if ( node.nodeType === 1 ) { 621 | return false; 622 | } 623 | } 624 | return true; 625 | case 'nth': 626 | var first = match[2], last = match[3]; 627 | 628 | if ( first === 1 && last === 0 ) { 629 | return true; 630 | } 631 | 632 | var doneName = match[0], 633 | parent = elem.parentNode; 634 | 635 | if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { 636 | var count = 0; 637 | for ( node = parent.firstChild; node; node = node.nextSibling ) { 638 | if ( node.nodeType === 1 ) { 639 | node.nodeIndex = ++count; 640 | } 641 | } 642 | parent.sizcache = doneName; 643 | } 644 | 645 | var diff = elem.nodeIndex - last; 646 | if ( first === 0 ) { 647 | return diff === 0; 648 | } else { 649 | return ( diff % first === 0 && diff / first >= 0 ); 650 | } 651 | } 652 | }, 653 | ID: function(elem, match){ 654 | return elem.nodeType === 1 && elem.getAttribute("id") === match; 655 | }, 656 | TAG: function(elem, match){ 657 | return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; 658 | }, 659 | CLASS: function(elem, match){ 660 | return (" " + (elem.className || elem.getAttribute("class")) + " ") 661 | .indexOf( match ) > -1; 662 | }, 663 | ATTR: function(elem, match){ 664 | var name = match[1], 665 | result = Expr.attrHandle[ name ] ? 666 | Expr.attrHandle[ name ]( elem ) : 667 | elem[ name ] != null ? 668 | elem[ name ] : 669 | elem.getAttribute( name ), 670 | value = result + "", 671 | type = match[2], 672 | check = match[4]; 673 | 674 | return result == null ? 675 | type === "!=" : 676 | type === "=" ? 677 | value === check : 678 | type === "*=" ? 679 | value.indexOf(check) >= 0 : 680 | type === "~=" ? 681 | (" " + value + " ").indexOf(check) >= 0 : 682 | !check ? 683 | value && result !== false : 684 | type === "!=" ? 685 | value !== check : 686 | type === "^=" ? 687 | value.indexOf(check) === 0 : 688 | type === "$=" ? 689 | value.substr(value.length - check.length) === check : 690 | type === "|=" ? 691 | value === check || value.substr(0, check.length + 1) === check + "-" : 692 | false; 693 | }, 694 | POS: function(elem, match, i, array){ 695 | var name = match[2], filter = Expr.setFilters[ name ]; 696 | 697 | if ( filter ) { 698 | return filter( elem, i, match, array ); 699 | } 700 | } 701 | } 702 | }; 703 | 704 | var origPOS = Expr.match.POS, 705 | fescape = function(all, num){ 706 | return "\\" + (num - 0 + 1); 707 | }; 708 | 709 | for ( var type in Expr.match ) { 710 | Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); 711 | Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); 712 | } 713 | 714 | var makeArray = function(array, results) { 715 | array = Array.prototype.slice.call( array, 0 ); 716 | 717 | if ( results ) { 718 | results.push.apply( results, array ); 719 | return results; 720 | } 721 | 722 | return array; 723 | }; 724 | 725 | // Perform a simple check to determine if the browser is capable of 726 | // converting a NodeList to an array using builtin methods. 727 | // Also verifies that the returned array holds DOM nodes 728 | // (which is not the case in the Blackberry browser) 729 | try { 730 | Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; 731 | 732 | // Provide a fallback method if it does not work 733 | } catch(e){ 734 | makeArray = function(array, results) { 735 | var ret = results || [], i = 0; 736 | 737 | if ( toString.call(array) === "[object Array]" ) { 738 | Array.prototype.push.apply( ret, array ); 739 | } else { 740 | if ( typeof array.length === "number" ) { 741 | for ( var l = array.length; i < l; i++ ) { 742 | ret.push( array[i] ); 743 | } 744 | } else { 745 | for ( ; array[i]; i++ ) { 746 | ret.push( array[i] ); 747 | } 748 | } 749 | } 750 | 751 | return ret; 752 | }; 753 | } 754 | 755 | var sortOrder, siblingCheck; 756 | 757 | if ( document.documentElement.compareDocumentPosition ) { 758 | sortOrder = function( a, b ) { 759 | if ( a === b ) { 760 | hasDuplicate = true; 761 | return 0; 762 | } 763 | 764 | if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { 765 | return a.compareDocumentPosition ? -1 : 1; 766 | } 767 | 768 | return a.compareDocumentPosition(b) & 4 ? -1 : 1; 769 | }; 770 | } else { 771 | sortOrder = function( a, b ) { 772 | var ap = [], bp = [], aup = a.parentNode, bup = b.parentNode, 773 | cur = aup, al, bl; 774 | 775 | // The nodes are identical, we can exit early 776 | if ( a === b ) { 777 | hasDuplicate = true; 778 | return 0; 779 | 780 | // If the nodes are siblings (or identical) we can do a quick check 781 | } else if ( aup === bup ) { 782 | return siblingCheck( a, b ); 783 | 784 | // If no parents were found then the nodes are disconnected 785 | } else if ( !aup ) { 786 | return -1; 787 | 788 | } else if ( !bup ) { 789 | return 1; 790 | } 791 | 792 | // Otherwise they're somewhere else in the tree so we need 793 | // to build up a full list of the parentNodes for comparison 794 | while ( cur ) { 795 | ap.unshift( cur ); 796 | cur = cur.parentNode; 797 | } 798 | 799 | cur = bup; 800 | 801 | while ( cur ) { 802 | bp.unshift( cur ); 803 | cur = cur.parentNode; 804 | } 805 | 806 | al = ap.length; 807 | bl = bp.length; 808 | 809 | // Start walking down the tree looking for a discrepancy 810 | for ( var i = 0; i < al && i < bl; i++ ) { 811 | if ( ap[i] !== bp[i] ) { 812 | return siblingCheck( ap[i], bp[i] ); 813 | } 814 | } 815 | 816 | // We ended someplace up the tree so do a sibling check 817 | return i === al ? 818 | siblingCheck( a, bp[i], -1 ) : 819 | siblingCheck( ap[i], b, 1 ); 820 | }; 821 | 822 | siblingCheck = function( a, b, ret ) { 823 | if ( a === b ) { 824 | return ret; 825 | } 826 | 827 | var cur = a.nextSibling; 828 | 829 | while ( cur ) { 830 | if ( cur === b ) { 831 | return -1; 832 | } 833 | 834 | cur = cur.nextSibling; 835 | } 836 | 837 | return 1; 838 | }; 839 | } 840 | 841 | // Utility function for retreiving the text value of an array of DOM nodes 842 | Sizzle.getText = function( elems ) { 843 | var ret = "", elem; 844 | 845 | for ( var i = 0; elems[i]; i++ ) { 846 | elem = elems[i]; 847 | 848 | // Get the text from text nodes and CDATA nodes 849 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) { 850 | ret += elem.nodeValue; 851 | 852 | // Traverse everything else, except comment nodes 853 | } else if ( elem.nodeType !== 8 ) { 854 | ret += Sizzle.getText( elem.childNodes ); 855 | } 856 | } 857 | 858 | return ret; 859 | }; 860 | 861 | // Check to see if the browser returns elements by name when 862 | // querying by getElementById (and provide a workaround) 863 | (function(){ 864 | // We're going to inject a fake input element with a specified name 865 | var form = document.createElement("div"), 866 | id = "script" + (new Date()).getTime(); 867 | form.innerHTML = ""; 868 | 869 | // Inject it into the root element, check its status, and remove it quickly 870 | var root = document.documentElement; 871 | root.insertBefore( form, root.firstChild ); 872 | 873 | // The workaround has to do additional checks after a getElementById 874 | // Which slows things down for other browsers (hence the branching) 875 | if ( document.getElementById( id ) ) { 876 | Expr.find.ID = function(match, context, isXML){ 877 | if ( typeof context.getElementById !== "undefined" && !isXML ) { 878 | var m = context.getElementById(match[1]); 879 | return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; 880 | } 881 | }; 882 | 883 | Expr.filter.ID = function(elem, match){ 884 | var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); 885 | return elem.nodeType === 1 && node && node.nodeValue === match; 886 | }; 887 | } 888 | 889 | root.removeChild( form ); 890 | root = form = null; // release memory in IE 891 | })(); 892 | 893 | (function(){ 894 | // Check to see if the browser returns only elements 895 | // when doing getElementsByTagName("*") 896 | 897 | // Create a fake element 898 | var div = document.createElement("div"); 899 | div.appendChild( document.createComment("") ); 900 | 901 | // Make sure no comments are found 902 | if ( div.getElementsByTagName("*").length > 0 ) { 903 | Expr.find.TAG = function(match, context){ 904 | var results = context.getElementsByTagName(match[1]); 905 | 906 | // Filter out possible comments 907 | if ( match[1] === "*" ) { 908 | var tmp = []; 909 | 910 | for ( var i = 0; results[i]; i++ ) { 911 | if ( results[i].nodeType === 1 ) { 912 | tmp.push( results[i] ); 913 | } 914 | } 915 | 916 | results = tmp; 917 | } 918 | 919 | return results; 920 | }; 921 | } 922 | 923 | // Check to see if an attribute returns normalized href attributes 924 | div.innerHTML = ""; 925 | if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && 926 | div.firstChild.getAttribute("href") !== "#" ) { 927 | Expr.attrHandle.href = function(elem){ 928 | return elem.getAttribute("href", 2); 929 | }; 930 | } 931 | 932 | div = null; // release memory in IE 933 | })(); 934 | 935 | if ( document.querySelectorAll ) { 936 | (function(){ 937 | var oldSizzle = Sizzle, div = document.createElement("div"); 938 | div.innerHTML = "

"; 939 | 940 | // Safari can't handle uppercase or unicode characters when 941 | // in quirks mode. 942 | if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { 943 | return; 944 | } 945 | 946 | Sizzle = function(query, context, extra, seed){ 947 | context = context || document; 948 | 949 | // Only use querySelectorAll on non-XML documents 950 | // (ID selectors don't work in non-HTML documents) 951 | if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) { 952 | try { 953 | return makeArray( context.querySelectorAll(query), extra ); 954 | } catch(e){} 955 | } 956 | 957 | return oldSizzle(query, context, extra, seed); 958 | }; 959 | 960 | for ( var prop in oldSizzle ) { 961 | Sizzle[ prop ] = oldSizzle[ prop ]; 962 | } 963 | 964 | div = null; // release memory in IE 965 | })(); 966 | } 967 | 968 | (function(){ 969 | var div = document.createElement("div"); 970 | 971 | div.innerHTML = "
"; 972 | 973 | // Opera can't find a second classname (in 9.6) 974 | // Also, make sure that getElementsByClassName actually exists 975 | if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { 976 | return; 977 | } 978 | 979 | // Safari caches class attributes, doesn't catch changes (in 3.2) 980 | div.lastChild.className = "e"; 981 | 982 | if ( div.getElementsByClassName("e").length === 1 ) { 983 | return; 984 | } 985 | 986 | Expr.order.splice(1, 0, "CLASS"); 987 | Expr.find.CLASS = function(match, context, isXML) { 988 | if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { 989 | return context.getElementsByClassName(match[1]); 990 | } 991 | }; 992 | 993 | div = null; // release memory in IE 994 | })(); 995 | 996 | function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { 997 | for ( var i = 0, l = checkSet.length; i < l; i++ ) { 998 | var elem = checkSet[i]; 999 | if ( elem ) { 1000 | elem = elem[dir]; 1001 | var match = false; 1002 | 1003 | while ( elem ) { 1004 | if ( elem.sizcache === doneName ) { 1005 | match = checkSet[elem.sizset]; 1006 | break; 1007 | } 1008 | 1009 | if ( elem.nodeType === 1 && !isXML ){ 1010 | elem.sizcache = doneName; 1011 | elem.sizset = i; 1012 | } 1013 | 1014 | if ( elem.nodeName.toLowerCase() === cur ) { 1015 | match = elem; 1016 | break; 1017 | } 1018 | 1019 | elem = elem[dir]; 1020 | } 1021 | 1022 | checkSet[i] = match; 1023 | } 1024 | } 1025 | } 1026 | 1027 | function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { 1028 | for ( var i = 0, l = checkSet.length; i < l; i++ ) { 1029 | var elem = checkSet[i]; 1030 | if ( elem ) { 1031 | elem = elem[dir]; 1032 | var match = false; 1033 | 1034 | while ( elem ) { 1035 | if ( elem.sizcache === doneName ) { 1036 | match = checkSet[elem.sizset]; 1037 | break; 1038 | } 1039 | 1040 | if ( elem.nodeType === 1 ) { 1041 | if ( !isXML ) { 1042 | elem.sizcache = doneName; 1043 | elem.sizset = i; 1044 | } 1045 | if ( typeof cur !== "string" ) { 1046 | if ( elem === cur ) { 1047 | match = true; 1048 | break; 1049 | } 1050 | 1051 | } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { 1052 | match = elem; 1053 | break; 1054 | } 1055 | } 1056 | 1057 | elem = elem[dir]; 1058 | } 1059 | 1060 | checkSet[i] = match; 1061 | } 1062 | } 1063 | } 1064 | 1065 | Sizzle.contains = document.compareDocumentPosition ? function(a, b){ 1066 | return !!(a.compareDocumentPosition(b) & 16); 1067 | } : function(a, b){ 1068 | return a !== b && (a.contains ? a.contains(b) : true); 1069 | }; 1070 | 1071 | Sizzle.isXML = function(elem){ 1072 | // documentElement is verified for cases where it doesn't yet exist 1073 | // (such as loading iframes in IE - #4833) 1074 | var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; 1075 | return documentElement ? documentElement.nodeName !== "HTML" : false; 1076 | }; 1077 | 1078 | var posProcess = function(selector, context){ 1079 | var tmpSet = [], later = "", match, 1080 | root = context.nodeType ? [context] : context; 1081 | 1082 | // Position selectors must be done after the filter 1083 | // And so must :not(positional) so we move all PSEUDOs to the end 1084 | while ( (match = Expr.match.PSEUDO.exec( selector )) ) { 1085 | later += match[0]; 1086 | selector = selector.replace( Expr.match.PSEUDO, "" ); 1087 | } 1088 | 1089 | selector = Expr.relative[selector] ? selector + "*" : selector; 1090 | 1091 | for ( var i = 0, l = root.length; i < l; i++ ) { 1092 | Sizzle( selector, root[i], tmpSet ); 1093 | } 1094 | 1095 | return Sizzle.filter( later, tmpSet ); 1096 | }; 1097 | 1098 | // EXPOSE 1099 | 1100 | window.Sizzle = Sizzle; 1101 | 1102 | })(); 1103 | -------------------------------------------------------------------------------- /src/table/__init__.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Table plugin initialization script 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | var R = RightJS, 7 | $ = RightJS.$, 8 | $E = RightJS.$E, 9 | isHash = RightJS.isHash, 10 | isElement = RightJS.isElement, 11 | Class = RightJS.Class, 12 | Object = RightJS.Object, 13 | Element = RightJS.Element; 14 | 15 | 16 | include_module_files( 17 | 'table', 18 | 'document' 19 | ); -------------------------------------------------------------------------------- /src/table/document.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Document level hooks for the table plugin 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | $(document).onClick(function(event) { 7 | var th = event.find('th.sortable'); 8 | 9 | if (th) { 10 | th.parent('table').sort(th); 11 | } 12 | }); -------------------------------------------------------------------------------- /src/table/table.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tables specific dom-wrapper 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var Table = Element.Wrappers.TABLE = new Class(Element, { 7 | 8 | extend: { 9 | version: '2.2.0', 10 | 11 | Options: { 12 | ascMarker: '▼', // asc marker content 13 | descMarker: '▲', // desc marker content 14 | algorithm: 'text', // default sorting algorithm 'text' or 'numeric' 15 | order: 'asc', // default order 16 | sortedClass: 'sorted' 17 | } 18 | }, 19 | 20 | /** 21 | * Basic constructor 22 | * 23 | * @param mixed raw dom-element or an options list 24 | * @return void 25 | */ 26 | initialize: function(arg) { 27 | var options = arg || {}, element = options; 28 | 29 | if (isHash(options) && !isElement(options)) { 30 | element = 'table'; 31 | } 32 | 33 | this.$super(element, options); 34 | 35 | this.options = Object.merge( 36 | Table.Options, new Function('return '+ this.get('data-table')) 37 | ); 38 | }, 39 | 40 | /** 41 | * Sorts the table by the given index 42 | * 43 | * @param mixed Number column index or TH element 44 | * @param String order direction 'asc' or 'desc' 45 | * @param String optional algorythm 'string', 'numeric' 46 | * @return Table this 47 | */ 48 | sort: function(index, direction, algorithm) { 49 | var th = index instanceof Element ? index : this.header().last().children('th')[index]; 50 | if (!th) { return this; } // in case something goes wrong 51 | 52 | // reading data from the TH cell 53 | index = th.parent().children('th').indexOf(th); 54 | direction = direction || (this.marker && this.marker.parent() === th ? this.marker.asc ? 'desc' : 'asc' : null); 55 | algorithm = algorithm || (th.hasClass('numeric') ? 'numeric' : null); 56 | 57 | // handling the fallback from the options 58 | direction = direction || this.options.order; 59 | algorithm = algorithm || this.options.algorithm; 60 | sortedClass = this.options.sortedClass; 61 | 62 | // collecting the list of sortable rows 63 | var rows = this.rows().map(function(row) { 64 | var cell = row.children('td')[index], text = cell ? cell.text() : ''; 65 | 66 | if (algorithm === 'numeric') { 67 | text = R(text).toFloat(); 68 | } 69 | 70 | return { row: row, text: text }; 71 | }); 72 | 73 | // creating an anchor where to insert the rows 74 | var anchor = rows[0] ? 75 | $E('tr').insertTo(rows[0].row, 'before') : 76 | $E('tr').insertTo(this.first('tbody') || this); 77 | 78 | // finding the exact sorting algorithm 79 | if (typeof(algorithm) !== 'function') { 80 | algorithm = direction === 'asc' ? function(a, b) { 81 | return a.text > b.text ? 1 : a.text < b.text ? -1 : 0; 82 | } : function(a, b) { 83 | return a.text > b.text ? -1 : a.text < b.text ? 1 : 0; 84 | }; 85 | } 86 | 87 | // sorting the rows and reinsert them in the correct order 88 | rows.sort(algorithm).reverse().each(function(hash) { 89 | anchor.insert(hash.row, 'after'); 90 | }); 91 | 92 | anchor.remove(); 93 | 94 | // putting the sorting marker 95 | this.marker = ( 96 | this.marker || th.first('span.sort-marker') || $E('span', {'class': 'sort-marker'}) 97 | ).update( 98 | this.options[direction === 'asc' ? 'ascMarker' : 'descMarker'] 99 | ).insertTo(th, 'bottom'); 100 | this.marker.asc = direction === 'asc'; 101 | this.find('th').each(function(th) { 102 | th.removeClass(sortedClass); 103 | }); 104 | this.marker.parent().toggleClass(sortedClass); 105 | 106 | return this; 107 | }, 108 | 109 | /** 110 | * Returns the table data-rows 111 | * 112 | * @return Array table data rows 113 | */ 114 | rows: function() { 115 | return this.find('tr').reject(function(row) { 116 | return row.first('th') || row.parent('tfoot'); 117 | }); 118 | }, 119 | 120 | /** 121 | * Returns the table header rows 122 | * 123 | * @return Array table header rows 124 | */ 125 | header: function() { 126 | return this.find('tr').filter('first', 'th'); 127 | }, 128 | 129 | /** 130 | * Returns the table footer rows 131 | * 132 | * @return Array table footer rows 133 | */ 134 | footer: function() { 135 | return this.find('tfoot > tr'); 136 | } 137 | }); -------------------------------------------------------------------------------- /test/casting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS - Casting 5 | 6 | 184 | 185 | 186 |
187 | 188 |
189 | 190 | 191 |
192 | 193 | 194 | 195 |
196 | 197 |
198 | 199 | -------------------------------------------------------------------------------- /test/dnd.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS - Drag'n'Drops Test 5 | 6 | 7 | 8 | 52 | 53 | 54 |

Simple draggables

55 |

56 |

57 |
drag me around
58 |
drag me too!
59 |
or me!
60 |
61 | 68 |

69 | 70 |

Revertable Draggables

71 |

72 |

73 |
I move back
74 |
I do it faster!
75 |
I do it instantly!
76 |
77 | 84 |

85 | 86 |

Cloned Draggables

87 |

88 |

89 |
I leave clone
90 |
So do I
91 |
Me too!
92 |
93 | 100 |

101 | 102 |

Snapping Draggables

103 |

104 |

105 |
I snap to 40px
106 |
I snap to [40, 0]
107 |
I snap to [0, 40]
108 |
109 | 116 |

117 | 118 |

Axis Draggables

119 |

120 |

121 |
I move around
122 |
I move only X
123 |
I move only Y
124 |
125 | 132 |

133 | 134 |

Ranged Draggables

135 |

136 | Ranged by simple positions 137 |

138 |
I move 100px
139 |
I move 200px
140 |
I move [200, 100]
141 |
142 | 168 |

169 |

170 | Range defined by another element 171 |

172 |
I move only here
173 |
174 | 175 |
176 |
Seems like me too
177 |
178 | 179 | 185 |

186 | 187 |

Relatively Positioned Draggable

188 |

189 |

190 |
Move me around
191 |
192 | 197 |

198 | 199 |

Auto-Discovered Draggables

200 |

201 |

202 |
I move around
203 |
I move only X
204 |
I move only Y
205 |
206 |

207 | 208 |

 

209 | 210 |

Simple Droppable

211 |

212 |

213 |
Drag me
214 |
Drop here
215 |
or here
216 |
217 | 229 |

230 | 231 |

Overlapping Droppables

232 |

233 | Those droppables will react only when overlapped with draggable (50% by default) 234 |

235 |
Drag me
236 |
X Only Overlap
237 |
Y Only Overlap
238 |
239 |

240 |

241 |

242 |
Both Overlap
243 |
Overlap Y 30%
244 |
Overlap X 70%
245 |
246 | 247 | 256 |

257 | 258 |

Selective Droppables

259 |

260 | Selective by a css-rule 261 |

262 |
Drag me
263 |
Reds Only
264 |
Blues Only
265 |
266 |

267 |

268 |

269 |
Drag me
270 |
Reds Only
271 |
Blues Only
272 |
273 | 274 | 282 |

283 | 284 |

285 | Selective by an elements list 286 |

287 |
First
288 |
First Only
289 |
Second Only
290 |
291 |

292 |

293 |

294 |
Second
295 |
First Only
296 |
Second Only
297 |
298 | 299 | 307 |

308 | 309 |

Automatically Discovered Droppables

310 |

311 |

312 |
First
313 |
Drop it here
314 |
not here
315 |
316 |

317 | 318 |

319 | 320 | -------------------------------------------------------------------------------- /test/effects.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS - Additional FX Test 5 | 6 | 7 | 8 | 29 | 30 | 31 |

Fx.Move

32 |

33 |

I move
34 | right | 35 | left | 36 | top | 37 | bottom 38 |

39 | 40 |

Fx.Run

41 |

42 |

43 |
I run
44 |
45 | run left | 46 | run top 47 |

48 | 49 |

Fx.Zoom

50 |

51 |

I zoom
52 | x1.5 zoom | 53 | x0.6 minify 54 |

55 | 56 |

Fx.Bounce

57 |

58 |

I bounce
59 | make it bounce 60 |

61 | 62 |

Fx.Puff

63 |

64 |

65 |
I puff
66 |
67 | puff it 68 |

69 | 70 |

Fx.Glow

71 |

72 |

73 |
I glow
74 |
75 | make it glow 76 |

77 | 78 | 79 |

80 | 81 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS Plugins - Test 5 | 6 | 18 | 19 | -------------------------------------------------------------------------------- /test/jquerysh.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS - jQuerysh Test 5 | 6 | 7 | 58 | 59 | -------------------------------------------------------------------------------- /test/json/json_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The JSON export/import functionality test 3 | * 4 | * @copyright (C) 2009-2010 Nikolay V. Nemshilov aka St. 5 | */ 6 | var JsonTest = TestCase.create({ 7 | name: "JsonTest", 8 | 9 | testNullToJSON: function() { 10 | this.assertEqual('null', JSON.stringify(null)); 11 | }, 12 | 13 | testStringToJSON: function() { 14 | this.assertEqual('"boo"', JSON.stringify("boo")); 15 | 16 | this.assertEqual('"\\\\b"', JSON.stringify("\\b")); 17 | this.assertEqual('"\\\\t"', JSON.stringify("\\t")); 18 | this.assertEqual('"\\\\n"', JSON.stringify('\\n')); 19 | this.assertEqual('"\\\\r"', JSON.stringify('\\r')); 20 | this.assertEqual('"\\\\f"', JSON.stringify('\\f')); 21 | this.assertEqual('"\\\\"', JSON.stringify('\\')); 22 | this.assertEqual('"\\""', JSON.stringify('"')); 23 | 24 | this.assertEqual('"\\\\ufff0"', JSON.stringify("\\ufff0")); 25 | this.assertEqual('"\\\\uffff"', JSON.stringify("\\uffff")); 26 | }, 27 | 28 | testDateToJSON: function() { 29 | var date = new Date(); 30 | date.setMilliseconds(888); 31 | date.setSeconds(8); 32 | date.setMinutes(8); 33 | date.setHours(8); 34 | date.setDate(8); 35 | date.setMonth(8); 36 | date.setYear(2008); 37 | 38 | this.assertEqual('"2008-09-08T04:08:08.888Z"', JSON.stringify(date)); 39 | }, 40 | 41 | testNumberToJSON: function() { 42 | this.assertEqual('8', JSON.stringify(8)); 43 | this.assertEqual('8.8', JSON.stringify(8.8)); 44 | this.assertEqual('-8.8', JSON.stringify(-8.8)); 45 | }, 46 | 47 | testBooleanToJSON: function() { 48 | this.assertEqual('true', JSON.stringify(true)); 49 | this.assertEqual('false', JSON.stringify(false)); 50 | }, 51 | 52 | testArrayToJSON: function() { 53 | this.assertEqual('[1,2,3]', JSON.stringify([1,2,3])); 54 | this.assertEqual('["a","b","c"]', JSON.stringify(RightJS.$w('a b c'))); 55 | 56 | this.assertEqual('["a",["b",["c"]]]', JSON.stringify(['a',['b',['c']]])); 57 | }, 58 | 59 | testObjectToJson: function() { 60 | this.assertEqual('{"a":"a1","b":"b1"}', JSON.encode({a:'a1',b:'b1'})); 61 | 62 | this.assertEqual( 63 | '{"a":[1,{"b":1}],"b":1,"c":false,"d":null,"e":{"f":"g"}}', 64 | JSON.encode({a:[1,{b:1}],b:1,c:false,d:null,e:{f:'g'}}) 65 | ); 66 | }, 67 | 68 | testJsonParse: function() { 69 | this.assertEqual( 70 | {a:[1,{b:1}],b:1,c:false,d:null,e:{f:'g'}}, 71 | JSON.decode('{"a":[1,{"b":1}],"b":1,"c":false,"d":null,"e":{"f":"g"}}') 72 | ); 73 | }, 74 | 75 | testJsonParseError: function() { 76 | this.assertThrows(function() { 77 | JSON.decode('{123'); 78 | }) 79 | }, 80 | 81 | testCookieExtension: function() { 82 | if (RightJS.Cookie && RightJS.Cookie.enabled()) { 83 | RightJS.Cookie.set('__test1', {boo:'hoo'}); 84 | RightJS.Cookie.set('__test2', [1,2,3,4,5]); 85 | 86 | this.assertEqual({boo:'hoo'}, RightJS.Cookie.get('__test1')); 87 | this.assertEqual([1,2,3,4,5], RightJS.Cookie.get('__test2')); 88 | } 89 | }, 90 | 91 | testCookieWithNoValue: function() { 92 | if (RightJS.Cookie && RightJS.Cookie.enabled()) { 93 | this.assertNull(RightJS.Cookie.get('nonexistingname')); 94 | } 95 | } 96 | }); -------------------------------------------------------------------------------- /test/keys.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS - Keys Plugin Test 5 | 6 | 29 | 30 | -------------------------------------------------------------------------------- /test/lang/string_test.js: -------------------------------------------------------------------------------- 1 | var StringTest = TestCase.create({ 2 | name: 'StringTest', 3 | 4 | testTrim: function() { 5 | this.assertEqual('abc', ' abc '.trim()); 6 | this.assertEqual('', ' '.trim()); 7 | }, 8 | 9 | testTruncate: function() { 10 | this.assertEqual('fo...', 'foobar'.truncate(5)); 11 | this.assertEqual('foo..', 'foobar'.truncate(5, '..')); 12 | this.assertEqual('fooba', 'foobar'.truncate(5, '')); 13 | } 14 | }) -------------------------------------------------------------------------------- /test/rails/rails_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The ruby on rails extensions test 3 | * 4 | * Copyright (C) 2009 Nikolay V. Nemshilov aka St. 5 | */ 6 | var RailsTest = TestCase.create({ 7 | name: 'RailsTest', 8 | 9 | assertAliases: function(object, names) { 10 | if (RightJS.isString(names)) { 11 | RightJS.$w(names).each(function(name) { 12 | this.assert(object[RightJS(name).underscored()] === object[name], 13 | "checking alias for '"+name+"' -> '"+RightJS(name).underscored()+"'"); 14 | }, this); 15 | } else { 16 | for (new_name in names) { 17 | this.assert(object[new_name] === object[names[new_name]], "checking alias for '"+name+"' -> '"+new_name+"'"); 18 | } 19 | } 20 | 21 | }, 22 | 23 | testStringAliases: function() { 24 | this.assertAliases(RightJS.String.prototype, 'startsWith endsWith indexOf lastIndexOf evalScripts'); 25 | }, 26 | 27 | testArrayAliases: function() { 28 | this.assertAliases(RightJS.Array.prototype, 'sortBy indexOf lastIndexOf'); 29 | }, 30 | 31 | testGlobalAliases: function() { 32 | this.assertAliases(window, 'isArray isHash isFunction'); 33 | }, 34 | 35 | testOptionsAliases: function() { 36 | this.assertAliases(RightJS.Options, 'setOptions'); 37 | }, 38 | 39 | testObserverAliases: function() { 40 | this.assertAliases(RightJS.Observer.prototype, 'stopObserving'); 41 | }, 42 | 43 | testElementAliases: function() { 44 | this.assertAliases(RightJS.Element.prototype, 'addClass hasClass setClass removeClass'); 45 | }, 46 | 47 | testFormAliases: function() { 48 | this.assertAliases(RightJS.Form.prototype, 'onSubmit'); 49 | }, 50 | 51 | testFormElementAliases: function() { 52 | this.assertAliases(RightJS.Input.prototype, 'onChange'); 53 | }, 54 | 55 | testRubyAliases: function() { 56 | this.assertAliases(RightJS.String.prototype, { 57 | to_f: 'toFloat', 58 | to_i: 'toInt', 59 | gsub: 'replace', 60 | downcase: 'toLowerCase', 61 | upcase: 'toUpperCase', 62 | index: 'indexOf', 63 | rindex: 'lastIndexOf', 64 | strip: 'trim' 65 | }); 66 | 67 | this.assertAliases(RightJS.Array.prototype, { 68 | collect: 'map', 69 | detect: 'filter', 70 | index: 'indexOf', 71 | rindex: 'lastIndexOf' 72 | }); 73 | } 74 | }); -------------------------------------------------------------------------------- /test/sizzle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS - jQuerysh Test 5 | 6 | 7 | 40 | 41 | 42 | 43 | 48 | 49 | -------------------------------------------------------------------------------- /test/table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS - Table Test 5 | 6 | 7 | 39 | 40 | 41 |

Simple Table Sort

42 |

43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
Num.NamePriceLink
1Banana1.11/bananaShowDelete
2Apple1.01/appleShowDelete
3Orange1.10/orangeShowDelete
4Apricot2.00/apricotShowDelete
85 |

86 | 87 |

Table With Footer And Dashed Numbers

88 |

89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
Num.NamePriceLink
1 103 | Banana1-11/bananaShowDelete
2Apple1-01/appleShowDelete
3Orange1-10/orangeShowDelete
4Apricot2-00/apricotShowDelete
--4-22-
146 |

147 | 148 |

Empty Table

149 |

150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 |
Num.NamePriceLink
159 |

160 | 161 |

Programmatically Generated

162 |

163 |

164 |

165 | 211 | 212 |

213 | 214 | -------------------------------------------------------------------------------- /util/linter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A wrapper around JSLint to drop things into the console 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | var RightJS = require('./right-server.js'); 7 | var JSLint = require('./jslint').JSLINT; 8 | var fs = require('fs'); 9 | 10 | exports.Linter = new RightJS.Class({ 11 | extend: { 12 | Options: { 13 | debug: false, // no debug 14 | devel: false, // no console.log s 15 | evil: false, // no evals 16 | passfail: false, // don't stop on errors 17 | onevar: false, // allow more than one 'var' definition 18 | forin: true , // allow for in without ownershipt checks 19 | indent: 2 , // enforce 2 spaces indent 20 | maxerr: 12 , // max number of errors 21 | }, 22 | 23 | Okays: [ 24 | "Move 'var' declarations to the top of the function.", 25 | "Do not use 'new' for side effects.", 26 | "The Function constructor is eval." 27 | ] 28 | }, 29 | 30 | /** 31 | * Basic constructor 32 | * 33 | * @param {String} the source 34 | * @param {String} the linter options 35 | * @return void 36 | */ 37 | initialize: function(src, options) { 38 | this.source = src; 39 | this.options = options; 40 | }, 41 | 42 | /** 43 | * Runs the linter 44 | * 45 | * @return {Linter} this 46 | */ 47 | run: function() { 48 | var options = {}, okays = [], patches = ''; 49 | 50 | // extracting the additional options 51 | try { // skipping non-existing patch files 52 | patches = fs.readFileSync(this.options).toString(); 53 | } catch(e) {} 54 | 55 | eval(patches); 56 | 57 | JSLint.okays = this.constructor.Okays.concat(okays); 58 | 59 | JSLint( 60 | fs.readFileSync(this.source).toString(), 61 | Object.merge(this.constructor.Options, options) 62 | ); 63 | 64 | this.errors = JSLint.errors.compact(); 65 | this.failed = this.errors.length > 0; 66 | 67 | return this; 68 | }, 69 | 70 | /** 71 | * Prints out the check report 72 | * 73 | * @return {Linter} this 74 | */ 75 | report: function() { 76 | 77 | if (this.errors.empty()) { 78 | console.log("\u001B[32m - JSLint check successfully passed\u001B[0m"); 79 | } else { 80 | console.log("\u001B[31m - JSLint check failed in: "+ this.source + "\u001B[0m"); 81 | 82 | this.errors.each(function(error) { 83 | var report = "\n", j=0, pointer=''; 84 | for (; j < error.character-1; j++) { pointer += '-'; } 85 | 86 | report += " \u001B[35m"+ error.reason +"\u001B[0m "; 87 | 88 | if (error.evidence) { 89 | report += "Line: "+ error.line + ", Char: "+ error.character + "\n"; 90 | report += " "+ error.evidence + "\n"; 91 | report += " \u001B[33m"+ pointer + "^\u001B[0m"; 92 | } 93 | 94 | console.log(report); 95 | }); 96 | 97 | console.log("\n") 98 | } 99 | return this; 100 | } 101 | 102 | }); -------------------------------------------------------------------------------- /util/nake.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Nake is a 'make' or 'rake' like tasks manager for NodeJS 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | var Nake = exports; 7 | 8 | Nake.Version = '0.2.0'; 9 | Nake.Tasks = {}; 10 | Nake.Default = null; // default task to run 11 | Nake.Quiet = false; // log out the messages 12 | 13 | var Scope = ''; // current scope 14 | var Colors = Nake.Colors = { 15 | red: 31, 16 | green: 32, 17 | blue: 36 18 | }; 19 | var LogIndent = ''; // used for nested task calls 20 | var LogIndentStep = ' '; 21 | 22 | // Parsing the call arguments 23 | var args = Nake.ARGS = {}; 24 | 25 | process.ARGV.slice(2).forEach(function(key) { 26 | if (key.match(/^(-[a-z\-]+|[A-Z_]+=)/)) { 27 | key = key.split('='); 28 | Nake.ARGS[key[0]] = key[1] || true; 29 | } 30 | }); 31 | 32 | 33 | /** 34 | * The entry point for the tasks 35 | * 36 | * @return void 37 | */ 38 | Nake.start = function() { 39 | if (args['-l'] || args['--list']) { 40 | return Nake.printList(); 41 | } 42 | 43 | if (args['-h'] || args['--help']) { 44 | return Nake.printHelp(); 45 | } 46 | 47 | if (args['-q'] || args['--quiet']) { 48 | Nake.Quiet = true; 49 | } 50 | 51 | var argv = process.ARGV.slice(2); 52 | 53 | if (argv.length === 0) { 54 | argv = [Nake.Default]; 55 | } 56 | 57 | for (var i=0; i < argv.length; i++) { 58 | if (argv[i] in Nake.Tasks) { 59 | Nake.Tasks[argv[i]].run(); 60 | } 61 | } 62 | }; 63 | 64 | 65 | /** 66 | * The basic nake task unit 67 | * 68 | * @param {String} task name 69 | * @param {String} task description (optional) 70 | * @param {Function} the task callback function 71 | * @return void 72 | */ 73 | function Task(name, description, callback) { 74 | this.name = name; 75 | 76 | if (typeof(description) === 'function') { 77 | this.description = null; 78 | this.callback = description; 79 | } else { 80 | this.description = description; 81 | this.callback = callback; 82 | } 83 | 84 | Nake.Tasks[Scope + name] = this; 85 | }; 86 | 87 | /** 88 | * Runs the task 89 | * 90 | * @param {Boolean} optional quiet run flag 91 | * @return {Task} this 92 | */ 93 | Task.prototype.run = function(quiet) { 94 | if (typeof(quiet) === 'string') { 95 | this.step(quiet); 96 | } 97 | 98 | var old_quiet = Nake.Quiet; 99 | Nake.Quiet = quiet === undefined ? old_quiet : !!quiet; 100 | 101 | if (this.description) { 102 | this.title(this.description); 103 | } 104 | 105 | var old_log_indent = LogIndent; 106 | LogIndent += LogIndentStep; 107 | 108 | this.callback(); 109 | 110 | LogIndent = old_log_indent; 111 | 112 | if (this.description) { 113 | this.done(); 114 | } 115 | 116 | Nake.Quiet = old_quiet; 117 | 118 | return this; 119 | }; 120 | 121 | /** 122 | * Prints out the task title 123 | * 124 | * 125 | * @param {String} title text 126 | * @param {String} optional color 127 | * @return {Task} this 128 | */ 129 | Task.prototype.title = function(text, color) { 130 | print(ljust("== "+ text +" ", 80 - LogIndent.length, '='), color || 'blue'); 131 | return this; 132 | }; 133 | 134 | /** 135 | * Prints out the step the task currently working on 136 | * 137 | * @param {String} step name 138 | * @param {String} optional color 139 | * @return {Task} this 140 | */ 141 | Task.prototype.step = function(text, color) { 142 | print("\b\b - "+ text, color); 143 | return this; 144 | }; 145 | 146 | /** 147 | * Prints out the task-complete sign 148 | * 149 | * @param {String} optional text ('DONE' by default) 150 | * @param {String} optional label color ('green' by default) 151 | * @return {Task} this 152 | */ 153 | Task.prototype.done = function(text, color) { 154 | print(text || 'DONE', color || 'green'); 155 | return this; 156 | }; 157 | 158 | 159 | /** 160 | * A procedural style task creation 161 | * 162 | * @param {String} task name 163 | * @param {String} task description 164 | * @param {Function} the task callback function 165 | * @return {Task} new task object 166 | */ 167 | Nake.task = function(name, description, callback) { 168 | return new Task(name, description, callback); 169 | }; 170 | 171 | /** 172 | * All the tasks that are defined inside of the 173 | * callback will be defined in the given scope 174 | * 175 | * @param {String} scope name 176 | * @param {Function} callback 177 | * @return void 178 | */ 179 | Nake.namespace = function(name, callback) { 180 | var old_scope = Scope; 181 | Scope += name + ':'; 182 | 183 | callback(); 184 | 185 | Scope = old_scope; 186 | }; 187 | 188 | /** 189 | * Runs a task by name 190 | * 191 | * @param {String} task name 192 | * @param {Boolean} optional quiet run flag 193 | * @return {Task} the run task 194 | */ 195 | Nake.run = function(name, quiet) { 196 | if (!name in Nake.Tasks) { 197 | throw new Error("Nake: don't know a task named '"+ name + "'"); 198 | } 199 | 200 | return Nake.Tasks[name].run(quiet); 201 | }; 202 | 203 | /** 204 | * Prints the list of registered tasks 205 | * 206 | * @return void 207 | */ 208 | Nake.printList = function() { 209 | var tasks = {}, max_size = 0; 210 | 211 | for (var key in Nake.Tasks) { 212 | tasks[key] = Nake.Tasks[key]; 213 | 214 | if (key.length > max_size) { 215 | max_size = key.length; 216 | } 217 | } 218 | 219 | Task.prototype.title("REGISTERED TASKS"); 220 | 221 | for (var key in tasks) { 222 | console.log( 223 | 'nake ' + ljust(key, max_size) + ( 224 | Nake.Tasks[key].description ? 225 | colorize(' // ' + Nake.Tasks[key].description, '33') : '' 226 | ) 227 | ); 228 | } 229 | }; 230 | 231 | /** 232 | * Prints out the help text 233 | * 234 | * @return void 235 | */ 236 | Nake.printHelp = function() { 237 | console.log( 238 | "nake [-f filename] {options} tasknames... \n\n" + 239 | "Options are ... \n" + 240 | " -f, --file [FILE] Use FILE as the nakefile \n" + 241 | " -q, --quiet No default log messages \n" + 242 | " -l, --list List the known tasks \n" + 243 | " -h, --help Display this help " 244 | ); 245 | }; 246 | 247 | 248 | 249 | /////////////////////////////////////////////////////////////////////////////// 250 | // 251 | // All sorts of private stuff 252 | // 253 | /////////////////////////////////////////////////////////////////////////////// 254 | 255 | /** 256 | * Prints out the text with the color and according to the log indents 257 | * 258 | * @param {String} text 259 | * @param {String} optional color 260 | * @return void 261 | */ 262 | function print(text, color) { 263 | if (!Nake.Quiet) { 264 | console.log(LogIndent + colorize(text, color)); 265 | } 266 | } 267 | 268 | /** 269 | * Packs the given string to the given size 270 | * 271 | * @param {String} string 272 | * @param {Numeric} size 273 | * @return {String} result 274 | */ 275 | function ljust(str, size, filler) { 276 | filler = filler || ' '; 277 | 278 | while (str.length < size) { 279 | str += filler; 280 | } 281 | 282 | return str; 283 | } 284 | 285 | /** 286 | * Colorizes the string 287 | * 288 | * @param {String} the string 289 | * @param {String} color name or ASCII code 290 | * @return {String} with colors 291 | */ 292 | function colorize(str, color) { 293 | color = Colors[color] || color; 294 | 295 | return color ? "\u001B["+color+"m" + str + "\u001B[0m" : str; 296 | } -------------------------------------------------------------------------------- /util/source.js: -------------------------------------------------------------------------------- 1 | /** 2 | * An abstract interface to process source files 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | var RightJS = require('./right-server.js'); 7 | var Linter = require('./linter').Linter; 8 | var fs = require('fs'); 9 | 10 | exports.Source = new RightJS.Class({ 11 | 12 | /** 13 | * The Source constructor 14 | * 15 | * @param {Object} options 16 | * @return void 17 | */ 18 | initialize: function(options) { 19 | this.files = options.files || []; 20 | this.styles = options.styles || []; 21 | this.layout = options.layout || null; 22 | this.header = options.header || null; 23 | this.holders = options.holders || null; 24 | 25 | this.compile(); 26 | }, 27 | 28 | /** 29 | * Runs the Source compilation process 30 | * 31 | * @return {Srouce} this 32 | */ 33 | compile: function() { 34 | // compiling the basic source code 35 | this.source = this.files.map(function(filename) { 36 | return this.read( 37 | filename.endsWith('.js') ? filename : 38 | ('src/' + filename + '.js') 39 | ); 40 | }, this).join("\n\n"); 41 | 42 | // trying to embedd styles 43 | if (!this.styles.empty()) { 44 | this.source += this.embedStyles(this.styles); 45 | } 46 | 47 | // placing everything in a layout 48 | if (this.layout) { 49 | var layout = this.read(this.layout).split('%{source_code}'); 50 | this.source = layout[0] + this.source + layout[1]; 51 | } 52 | 53 | // filling in the placeholders 54 | for (var key in this.holders) { 55 | this.source = this.source 56 | .replace('%{'+ key +'}', this.holders[key]); 57 | } 58 | 59 | // reading the header content 60 | this.header = this.read(this.header); 61 | 62 | // trying to embed the version number into the header 63 | var match = this.source.match(/version\s*(:|=)\s*('|")(.+?)\2/i); 64 | if (match) { 65 | this.header = this.header.replace('%{version}', match[3]); 66 | } 67 | }, 68 | 69 | /** 70 | * An interface to patch the compiled files manually 71 | * 72 | * @param {Function} callback 73 | * @return {Source} this 74 | */ 75 | patch: function(callback) { 76 | this.source = callback.call(this, this.source); 77 | return this; 78 | }, 79 | 80 | /** 81 | * Writes the compiled Source down in the filename 82 | * 83 | * @param {String} filename (relative to the build directory) 84 | * @return {Source} this 85 | */ 86 | write: function(filename) { 87 | if (!filename.includes('-server')) { 88 | filename += '-src'; 89 | } 90 | 91 | this.filename = filename + '.js'; 92 | 93 | fs.writeFileSync(this.filename, this.header + this.source); 94 | return this; 95 | }, 96 | 97 | /** 98 | * Checks this source against with the JSLint 99 | * 100 | * @param {String} lint options file 101 | * @return {Linter} with the results 102 | */ 103 | check: function(lintfile) { 104 | return this.linter(lintfile).run().report(); 105 | }, 106 | 107 | /** 108 | * Returns a linter object 109 | * 110 | * @param {String} lint options file 111 | * @return {Linter} no-executed linter 112 | */ 113 | linter: function(lintfile) { 114 | return new Linter(this.filename, lintfile); 115 | }, 116 | 117 | /** 118 | * Compresses the source code and writes it down into the file 119 | * 120 | * @return {Source} this 121 | */ 122 | compress: function() { 123 | var jsp = require('./ugly/parse-js'); 124 | var pro = require('./ugly/process'); 125 | var ast = jsp.parse(this.source); 126 | 127 | ast = pro.ast_mangle(ast); 128 | ast = pro.ast_squeeze(ast); 129 | 130 | var filename = this.filename.replace('-src', ''); 131 | 132 | fs.writeFileSync(filename, this.header + pro.gen_code(ast)); 133 | 134 | // making a GZIP to check the compression 135 | try { // ain't gonna work on win 136 | require('child_process').exec( 137 | 'gzip -c ' + filename + ' > '+ filename + '.gz' 138 | ); 139 | } catch(e) {}; 140 | 141 | 142 | return this; 143 | }, 144 | 145 | /** 146 | * Embedds the stylesheets into the main source 147 | * 148 | * @param {Array} sources list 149 | * @return {Source} this 150 | */ 151 | embedStyles: function(styles) { 152 | styles = styles.map(this.read).join("\n"); 153 | 154 | // preserving IE hacks 155 | styles = styles 156 | .replace(/\/\*\\\*\*\/:/g, '_ie8_s:') 157 | .replace(/\\9;/g, '_ie8_e;') 158 | 159 | // compacting the styles 160 | .replace(/\/\*[\S\s]*?\*\//img, '') 161 | .replace(/\n\s*\n/mg, "\n") 162 | .replace(/\s+/img, ' ') 163 | .replace(/\s*(\+|>|\||~|\{|\}|,|\)|\(|;|:|\*)\s*/img, '$1') 164 | .replace(/;\}/g, '}') 165 | .replace(/\)([^;}\s])/g, ') $1') 166 | .trim() 167 | 168 | // getting IE hacks back 169 | .replace(/([^\s])\*/g, '$1 *') 170 | .replace(/_ie8_s:/g, '/*\\\\**/:') 171 | .replace(/_ie8_e(;|})/g, '\\\\9$1') 172 | 173 | // escaping the quotes 174 | .replace(/"/, '\"'); 175 | 176 | 177 | // making the JavaScript embedding script 178 | return "\n\n"+ 179 | "var embed_style = document.createElement('style'), \n"+ 180 | " embed_rules = document.createTextNode(\""+ styles + "\"); \n"+ 181 | " \n"+ 182 | "embed_style.type = 'text/css'; \n"+ 183 | "document.getElementsByTagName('head')[0].appendChild(embed_style); \n"+ 184 | " \n"+ 185 | "if(embed_style.styleSheet) { \n"+ 186 | " embed_style.styleSheet.cssText = embed_rules.nodeValue; \n"+ 187 | "} else { \n"+ 188 | " embed_style.appendChild(embed_rules); \n"+ 189 | "} \n"; 190 | }, 191 | 192 | // protected 193 | 194 | /** 195 | * Shortcut for the Node's crappy fs-read 196 | * 197 | * @param {String} filename 198 | * @return {Strng} file content 199 | */ 200 | read: function(filename) { 201 | return fs.readFileSync(filename).toString(); 202 | } 203 | 204 | }); -------------------------------------------------------------------------------- /util/test-page.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: Verdana, Helvetica, sans-serif; 3 | font-size: 10pt; 4 | margin: 0; 5 | padding: 0; 6 | } 7 | html { 8 | background: #EEE; 9 | height: 100%; 10 | padding: 0 9em; 11 | } 12 | body { 13 | background: #FFF; 14 | min-height: 100%; 15 | border: 1px solid #DDD; 16 | border-top: none; 17 | border-bottom: none; 18 | padding: 0 30pt; 19 | _padding-right: 0; 20 | } 21 | 22 | h1, h2, h3, h4 { 23 | font-family: "Trebuchet MS", Garuda, sans-serif; 24 | margin-top: 1.5em; 25 | margin-bottom: 0; 26 | color: #333; 27 | font-weight: normal; 28 | } 29 | 30 | h1#header { 31 | display: block; 32 | position: relative; 33 | width: 100%; 34 | margin-left: -30pt; 35 | margin-top: 0; 36 | margin-bottom: 1em; 37 | display: block; 38 | background: #444; 39 | color: white; 40 | font-size: 2.4em; 41 | padding: .6em 30pt; 42 | _width: auto; 43 | border-bottom: 4pt solid #999; 44 | } 45 | h1#header a { 46 | position: absolute; 47 | top: 3.5em; 48 | right: 3.5em; 49 | font-size: 10pt; 50 | color: white; 51 | outline: none; 52 | } 53 | h1#header a.safe { 54 | top: 1em; 55 | } 56 | 57 | h1#header ul#modes { 58 | position: absolute; 59 | font-size: 10pt; 60 | right: 3.5em; 61 | top: 1em; 62 | margin: 0; 63 | padding: 0; 64 | } 65 | 66 | h1#header ul#modes a { 67 | position: static; 68 | bottom: auto; 69 | right: auto; 70 | } 71 | h1#header ul#modes li, 72 | h1#header ul#modes li a { 73 | color: #888; 74 | } 75 | h1#header ul#modes li.current, 76 | h1#header ul#modes li.current a { 77 | color: white; 78 | } 79 | 80 | a { 81 | color: brown; 82 | text-decoration: none; 83 | } 84 | a:hover { 85 | text-decoration: underline; 86 | } 87 | 88 | input[type=text], input[type=password], textarea { 89 | border: 1px solid #bbb; 90 | font-size: 1em; 91 | width: 16em; 92 | padding: .1em; 93 | -moz-border-radius: .2em; 94 | -webkit-border-radius: .2em; 95 | } 96 | 97 | textarea { 98 | width: 30em; 99 | height: 6em; 100 | } 101 | 102 | a img { 103 | border: 2px solid brown; 104 | padding: 2px; 105 | background: white; 106 | vertical-align: top; 107 | -moz-border-radius: .24em; 108 | -webkit-border-radius: .24em; 109 | } 110 | 111 | p.white-space { 112 | display: block; 113 | height: 12em; 114 | margin: 0; 115 | padding: 0; 116 | } 117 | 118 | div.output-monitor { 119 | margin-bottom: 1em; 120 | border: 1px solid #ccc; 121 | padding: 0 .2em; 122 | width: 6em; 123 | -moz-border-radius: .2em; 124 | -webkit-border-radius: .2em; 125 | } 126 | 127 | /** The testcase styles */ 128 | div#testcase-inline-report-block { 129 | display: block; 130 | position: static; 131 | border: none; 132 | width: auto; 133 | margin-top: -2em; 134 | _margin-right: 30pt; 135 | } 136 | 137 | div#testcase-inline-report-block div.title { 138 | border: none; 139 | background: none; 140 | } -------------------------------------------------------------------------------- /util/tools.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Various scripts loading tools 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var 7 | 8 | // figuring out the current environment 9 | current_location = document.location.toString().replace(/#.*?$/, ''), 10 | 11 | in_safe_mode = current_location.toString().match(/(\?|&)safe/), 12 | testing_builds = current_location.toString().match(/(\?|&)build/), 13 | 14 | javascripts = document.getElementsByTagName('script'), 15 | current_src = javascripts[0].getAttribute('src'), 16 | root_path = self.root_path || current_src.split('util/tools')[0]; 17 | 18 | /** 19 | * Includes the filename on the page 20 | * 21 | * @param String filename (without the '.js' suffix) 22 | * @return void 23 | */ 24 | function include_js(filename) { 25 | document.write(''); 26 | }; 27 | 28 | /** 29 | * Includes a stylesheet file on the page 30 | * 31 | * @param String filename (without the '.css' suffix) 32 | * @return void 33 | */ 34 | function include_css(filename) { 35 | document.write(''); 36 | }; 37 | 38 | /** 39 | * Includes the rightjs build and optional modules 40 | * 41 | * @param String module name 42 | * .............. 43 | * @return void 44 | */ 45 | function include_right_js() { 46 | include_js('util/right' + (in_safe_mode ? '-safe' : '')); 47 | 48 | for (var i=0; i < arguments.length; i++) { 49 | include_js('util/right-'+ arguments[i]); 50 | } 51 | }; 52 | 53 | /** 54 | * Builds a basic test-page structure 55 | * 56 | * @param String mode-switching links 57 | * @return void 58 | */ 59 | function build_test_page(links) { 60 | include_css('util/test-page'); 61 | 62 | if (!links) { 63 | var no_safe_location = current_location.replace(/(\?|&)safe=[^?&]+/, '').replace('.html&', '.html?'); 64 | var no_build_location = current_location.replace(/(\?|&)build=[^?&]+/, '').replace('.html&', '.html?'); 65 | 66 | links = '' + 67 | ''+ (in_safe_mode ? 'Normal Mode' : 'Safe Mode') + '' + 70 | ''+ (testing_builds ? 'Test Source' : 'Test Builds') + '' 73 | ; 74 | } 75 | 76 | var doc_title = self.document_title || document.title; 77 | 78 | if (in_safe_mode) doc_title += ' / Safe Mode'; 79 | if (testing_builds) doc_title += ' / Builds'; 80 | 81 | document.write('

'+ doc_title + links +'

'); 82 | }; 83 | 84 | /** 85 | * Initializes the core test-page 86 | * 87 | * @return void 88 | */ 89 | function initialize_core_test_page() { 90 | include_js('util/testcase'); 91 | 92 | 93 | if (in_safe_mode) { 94 | include_js("build/right-safe-src"); 95 | } else if (testing_builds) { 96 | include_js("build/right"); 97 | } else { 98 | include_js("src/__init__"); 99 | } 100 | 101 | var clean_link = current_location.replace(/\?.*?$/, ''); 102 | 103 | build_test_page(''); 108 | }; 109 | 110 | /** 111 | * Builds the page structure with headers and stuff 112 | * 113 | * @return void 114 | */ 115 | function initialize_test_page() { 116 | include_js('util/testcase'); 117 | include_right_js(); 118 | 119 | build_test_page(); 120 | }; 121 | 122 | /** 123 | * Loads up the modules 124 | * 125 | * @param String module name 126 | * .... 127 | * @return void 128 | */ 129 | var modules_queue = []; 130 | function load_modules() { 131 | if (testing_builds) { 132 | for (var i=0; i < arguments.length; i++) { 133 | include_js('build/right-'+ arguments[i].replace(/_/g, '-')); 134 | } 135 | } else { 136 | for (var i=0; i < arguments.length; i++) { 137 | modules_queue.push(arguments[i]); 138 | include_js('src/'+ arguments[i] + '/__init__'); 139 | } 140 | } 141 | }; 142 | 143 | /** 144 | * Hooks up the currently loading module file 145 | * 146 | * @param String file name 147 | * ..... 148 | * @return void 149 | */ 150 | function include_module_files() { 151 | var module = modules_queue.shift(); 152 | 153 | for (var i=0; i < arguments.length; i++) { 154 | include_js('src/'+ module + '/' + arguments[i]); 155 | } 156 | }; 157 | 158 | /** 159 | * Hooks up a list of files from the 'src/' directory 160 | * 161 | * @param String file name 162 | * ........ 163 | * @return void 164 | */ 165 | function include_sources() { 166 | for (var i=0; i < arguments.length; i++) { 167 | include_js('src/'+ arguments[i]); 168 | } 169 | }; 170 | 171 | /** 172 | * Includes the sourcefiles organized in a hash of 173 | * modules 174 | * 175 | * @param Object hash of modules 176 | * @return void 177 | */ 178 | function include_sources_by_modules(modules) { 179 | for (var name in modules) { 180 | include_sources.apply(this, modules[name]); 181 | } 182 | }; 183 | 184 | /** 185 | * Includes a shared piece of javascript from the 'lib/' directory 186 | * 187 | * @param String file name 188 | * ..... 189 | * @return void 190 | */ 191 | function include_shared_js() { 192 | for (var i=0; i < arguments.length; i++) { 193 | include_js('lib/'+ arguments[i]); 194 | } 195 | }; 196 | 197 | /** 198 | * Includes a shared css-file out of the 'lib/css/' directory 199 | * 200 | * @param String file name 201 | * ............ 202 | * @return void 203 | */ 204 | function include_shared_css() { 205 | for (var i=0; i < arguments.length; i++) { 206 | include_css('lib/css/'+ arguments[i]); 207 | } 208 | }; 209 | 210 | /** 211 | * Loads up and initializes the tests 212 | * 213 | * @param Object test definition 214 | * @return void 215 | */ 216 | function run_tests(tests) { 217 | var names = []; 218 | 219 | for (var name in tests) { 220 | names.push(name); 221 | include_js('test/'+ tests[name] + '_test'); 222 | } 223 | 224 | window.onload = function() { 225 | setTimeout(function() { 226 | eval('new TestSuite('+names.join(',')+').run()'); 227 | }, 50); 228 | }; 229 | }; 230 | 231 | // hooking up the UI module 232 | if (self.initialize_ui_test_page && self.current_module) { 233 | initialize_ui_test_page(); 234 | load_ui_modules(current_module); 235 | } 236 | 237 | // trying to autoload the current module 238 | var autoload_module = current_src.split('?')[1]; 239 | 240 | if (autoload_module) { 241 | initialize_test_page(); 242 | 243 | if (autoload_module !== 'core') { 244 | load_modules(autoload_module); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /util/ugly/squeeze-more.js: -------------------------------------------------------------------------------- 1 | var jsp = require("./parse-js"), 2 | pro = require("./process"), 3 | slice = jsp.slice, 4 | member = jsp.member, 5 | PRECEDENCE = jsp.PRECEDENCE, 6 | OPERATORS = jsp.OPERATORS; 7 | 8 | function ast_squeeze_more(ast) { 9 | var w = pro.ast_walker(), walk = w.walk; 10 | return w.with_walkers({ 11 | "call": function(expr, args) { 12 | if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) { 13 | // foo.toString() ==> foo+"" 14 | return [ "binary", "+", expr[1], [ "string", "" ]]; 15 | } 16 | } 17 | }, function() { 18 | return walk(ast); 19 | }); 20 | }; 21 | 22 | exports.ast_squeeze_more = ast_squeeze_more; 23 | --------------------------------------------------------------------------------