├── .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 |
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 |
67 | puff it
68 |
69 |
70 | Fx.Glow
71 |
72 |
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 |
44 | - One
45 | - Two
46 | - Three
47 |
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 | Num. |
46 | Name |
47 | Price |
48 | Link |
49 | |
50 |
51 |
52 |
53 | 1 |
54 | Banana |
55 | 1.11 |
56 | /banana |
57 | Show |
58 | Delete |
59 |
60 |
61 | 2 |
62 | Apple |
63 | 1.01 |
64 | /apple |
65 | Show |
66 | Delete |
67 |
68 |
69 | 3 |
70 | Orange |
71 | 1.10 |
72 | /orange |
73 | Show |
74 | Delete |
75 |
76 |
77 | 4 |
78 | Apricot |
79 | 2.00 |
80 | /apricot |
81 | Show |
82 | Delete |
83 |
84 |
85 |
86 |
87 | Table With Footer And Dashed Numbers
88 |
89 |
90 |
91 |
92 | Num. |
93 | Name |
94 | Price |
95 | Link |
96 | |
97 |
98 |
99 |
100 |
101 |
102 | 1
103 | | Banana |
104 | 1-11 |
105 | /banana |
106 | Show |
107 | Delete |
108 |
109 |
110 | 2 |
111 | Apple |
112 | 1-01 |
113 | /apple |
114 | Show |
115 | Delete |
116 |
117 |
118 | 3 |
119 | Orange |
120 | 1-10 |
121 | /orange |
122 | Show |
123 | Delete |
124 |
125 |
126 | 4 |
127 | Apricot |
128 | 2-00 |
129 | /apricot |
130 | Show |
131 | Delete |
132 |
133 |
134 |
135 |
136 |
137 | - |
138 | - |
139 | 4-22 |
140 | - |
141 | |
142 | |
143 |
144 |
145 |
146 |
147 |
148 | Empty Table
149 |
150 |
151 |
152 | Num. |
153 | Name |
154 | Price |
155 | Link |
156 | |
157 |
158 |
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('');
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 |
--------------------------------------------------------------------------------