├── .gitignore ├── CHANGELOG ├── LICENSE ├── Nakefile ├── README.md ├── dist ├── header.js ├── header.olds.js ├── header.safe.js ├── header.server.js ├── layout.js ├── layout.olds.js ├── layout.safe.js ├── layout.server.js └── lint.js ├── src ├── __init__.js ├── core │ ├── class.js │ ├── observer.js │ ├── options.js │ └── util.js ├── dom │ ├── browser.js │ ├── cookie.js │ ├── document.js │ ├── element.js │ ├── element │ │ ├── commons.js │ │ ├── dimensions.js │ │ ├── events.js │ │ ├── structs.js │ │ └── styles.js │ ├── event.js │ ├── event │ │ ├── delegation.js │ │ ├── focusin.js │ │ └── mouseio.js │ ├── form.js │ ├── input.js │ ├── ready.js │ ├── selector.js │ ├── string.js │ ├── window.js │ └── wrapper.js ├── fx │ ├── element.js │ ├── fx.js │ ├── fx │ │ ├── attr.js │ │ ├── fade.js │ │ ├── highlight.js │ │ ├── morph.js │ │ ├── scroll.js │ │ ├── slide.js │ │ └── twin.js │ └── string.js ├── lang │ ├── array.js │ ├── function.js │ ├── json.js │ ├── math.js │ ├── number.js │ ├── object.js │ ├── regexp.js │ └── string.js ├── olds │ ├── css.js │ ├── events.js │ ├── ie.js │ ├── konq.js │ └── loader.js ├── right.js └── xhr │ ├── element.js │ ├── form.js │ ├── xhr.js │ └── xhr │ ├── dummy.js │ ├── iframed.js │ └── jsonp.js ├── test ├── effects.html ├── form │ ├── form-cancel-xhr.html │ └── server.rb ├── index.html ├── mouseio.html ├── safe │ ├── core_test.js │ ├── dom_test.js │ └── lang_test.js ├── tests.js ├── tests.safe.js ├── tests.server.js ├── ujs-test.html └── unit │ ├── core │ ├── class_test.js │ ├── observer_test.js │ ├── options_test.js │ └── util_test.js │ ├── dom │ ├── browser_test.js │ ├── cookie_test.js │ ├── element │ │ ├── commons_test.js │ │ ├── dimensions_test.js │ │ ├── events_test.js │ │ ├── structs_test.js │ │ └── styles_test.js │ ├── element_test.js │ ├── event │ │ ├── bubbling_test.js │ │ └── delegation_test.js │ ├── event_test.js │ ├── form_test.js │ ├── input_test.js │ ├── ready_test.js │ ├── selector_test.js │ ├── window_test.js │ └── wrapper_test.js │ ├── fx │ ├── fx │ │ └── morph_test.js │ └── fx_test.js │ ├── lang │ ├── array_test.js │ ├── function_test.js │ ├── json_test.js │ ├── math_test.js │ ├── number_test.js │ ├── object_test.js │ ├── regexp_test.js │ └── string_test.js │ └── xhr │ ├── xhr │ ├── iframed_test.js │ └── jsonp_test.js │ └── xhr_test.js └── util ├── jslint.js ├── linter.js ├── nake.js ├── right-server.js ├── source.js ├── test-page.css ├── testcase.js ├── tools.js └── ugly ├── parse-js.js ├── process.js └── squeeze-more.js /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .DS_Store -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome Home! 2 | 3 | RightJS is a fine JavaScript framework that's designed to make you happy. 4 | It lives over here , go check it, you won't regret! 5 | 6 | 7 | ## How To Build 8 | 9 | To build the core on your own, you'll need [NodeJS](http://nodejs.org) and 10 | if you also have [npm](http://npmjs.org) you might want to install the 11 | [nake](https://github.com/MadRabbit/Nake) tools 12 | 13 | npm install nake 14 | 15 | After that either run `nake` 16 | 17 | nake build 18 | 19 | or, if you don't have [npm](http://npmjs.org), just run the `Nakefile` 20 | directly with [NodeJS](http://nodejs.org) 21 | 22 | node Nakefile build 23 | 24 | Try, `-l` or `--list` key to see which other tasks are available 25 | 26 | 27 | ## Options 28 | 29 | You can switch off some modules from the core by using the `OPTIONS` key 30 | 31 | nake build OPTIONS=no-olds,no-fx 32 | 33 | The list of available options is the following 34 | 35 | * `no-form` - no advanced form features 36 | * `no-events` - no events delegation features 37 | * `no-cookie` - no cookies module 38 | * `no-xhr` - no ajax support 39 | * `no-fx` - no visual effects module 40 | * `no-olds` - puts the old browsers support in a separated file 41 | 42 | 43 | ## License 44 | 45 | The code released under terms of the MIT License 46 | 47 | Copyright (C) 2008-2011 Nikolay Nemshilov -------------------------------------------------------------------------------- /dist/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RightJS v%{version} - http://rightjs.org 3 | * Released under the terms of MIT license 4 | * 5 | * Copyright (C) 2008-2012 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/header.olds.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The old browsers support module for RightJS 3 | * 4 | * Released under the terms of the MIT license 5 | * Visit http://rightjs.org for more details 6 | * 7 | * Copyright (C) 2008-2012 Nikolay Nemshilov 8 | */ 9 | -------------------------------------------------------------------------------- /dist/header.safe.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RightJS v%{version} safe-mode, http://rightjs.org 3 | * Released under terms of the MIT license 4 | * 5 | * Copyright (C) 2008-2012 Nikolay Nemshilov 6 | */ 7 | -------------------------------------------------------------------------------- /dist/header.server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RightJS v%{version} the server-side version 3 | * Released under terms of the MIT license 4 | * Visit http://rightjs.org for more details 5 | * 6 | * Copyright (C) 2008-2012 Nikolay Nemshilov 7 | */ 8 | 9 | -------------------------------------------------------------------------------- /dist/layout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The basic layout for RightJS builds 3 | * 4 | * Copyright (C) 2008-2011 Nikolay Nemshilov 5 | */ 6 | var RightJS = (function(window, document, Object, Array, String, Function, Number, Math, undefined) { 7 | 8 | %{source_code} 9 | 10 | // globalizing the top-level variables 11 | $ext(window, Object.without(RightJS, 'version', 'modules')); 12 | 13 | return RightJS; 14 | })(window, document, Object, Array, String, Function, Number, Math); 15 | -------------------------------------------------------------------------------- /dist/layout.olds.js: -------------------------------------------------------------------------------- 1 | (function(RightJS) { 2 | %{source_code} 3 | })(RightJS); -------------------------------------------------------------------------------- /dist/layout.safe.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The safe-mode layout 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var RightJS = (function(window, src) { 7 | // premassaging the source code, swapping the document reference where needed 8 | src = src 9 | // the default document to the current one 10 | .replace(',document,', ',parent.document,') 11 | 12 | // building the inside types conversion methods 13 | + 'RightJS.$N=function(v){return new Number(v)};' 14 | + 'RightJS.$S=function(v){return new String(v)}'; 15 | 16 | // building the frame sandbox 17 | var frame_id = '__rightjs_condom', document = window.document; 18 | if ('attachEvent' in window) { 19 | document.write(''); 20 | } else { 21 | var frame = document.createElement('iframe'); 22 | frame.name = frame_id; 23 | frame.style.display = 'none'; 24 | document.documentElement.appendChild(frame); 25 | } 26 | 27 | // puttin the code into the frame 28 | var win = window.frames[frame_id]; 29 | 30 | if ('execScript' in win) { 31 | win.execScript(src); 32 | } else { 33 | var doc = win.document; 34 | doc.open();doc.write(''); doc.close(); 35 | 36 | var script = document.createElement('script'); 37 | script.text = src; 38 | doc.body.appendChild(script); 39 | } 40 | 41 | // transferring the object references from the sandbox into local variable 42 | var RightJS = win.RightJS; 43 | RightJS.context = win; 44 | RightJS.safe = true; 45 | 46 | // building the access and types conversion proxy 47 | var proxy = function(value) { 48 | switch (typeof value) { 49 | case 'number': return RightJS.$N(value); 50 | case 'string': return RightJS.$S(value); 51 | case 'function': return RightJS.$ext(value, RightJS.Function.Methods); 52 | case 'object': 53 | if (RightJS.isArray(value)) 54 | return RightJS.$A(value); 55 | } 56 | 57 | return value; 58 | }; 59 | 60 | return RightJS.$ext(proxy, RightJS); 61 | 62 | })(window, %{source_code}); 63 | -------------------------------------------------------------------------------- /dist/layout.server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The server-side CommonJS builds layout 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | %{source_code} 7 | 8 | if (exports) { 9 | $ext(exports, RightJS); 10 | } 11 | -------------------------------------------------------------------------------- /dist/lint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JSLint additional options 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var options = {}; 7 | var okays = [ 8 | ' current_Document.win()._.execScript(text);', 9 | ' return value != null && value.nodeType === 1;', 10 | ' return value != null && value.nodeType != null;', 11 | ' value != null && value.hasOwnProperty != null;', 12 | " event.target != null && 'nodeType' in event.target && event.target.nodeType === 3 ?", 13 | ' if (object != null) {', 14 | ' return this == false;', 15 | 'Do not use Number as a constructor.', 16 | "Expected a 'break' statement before 'case'.", 17 | "The Function constructor is eval.", 18 | 'var RightJS = (function(window, document, Object, Array, String, Function, Number, Math, undefined) {' 19 | ]; -------------------------------------------------------------------------------- /src/__init__.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The source code initialization script 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | include_sources_by_modules({ 7 | core: [ 8 | 'right', 9 | 10 | // the core utils should be included before the language extentions 11 | 'core/util', 12 | 13 | 'lang/object', 14 | 'lang/math', 15 | 'lang/array', 16 | 'lang/string', 17 | 'lang/function', 18 | 'lang/number', 19 | 'lang/regexp', 20 | 'lang/json', 21 | 22 | 'core/class', 23 | 'core/options', 24 | 'core/observer' 25 | ], 26 | 27 | dom: [ 28 | 'dom/browser', 29 | 'dom/wrapper', 30 | 31 | 'dom/document', 32 | 'dom/window', 33 | 34 | 'dom/event', 35 | 36 | 'dom/element', 37 | 'dom/element/structs', 38 | 'dom/element/styles', 39 | 'dom/element/commons', 40 | 'dom/element/dimensions', 41 | 'dom/element/events', 42 | 43 | 'dom/selector', 44 | 'dom/ready' 45 | ], 46 | 47 | form: [ 48 | 'dom/form', 49 | 'dom/input' 50 | ], 51 | 52 | // NOTE: this one should be after the 'form' module! 53 | events: [ 54 | 'dom/event/focusin', 55 | 'dom/event/mouseio', 56 | 'dom/event/delegation', 57 | 58 | 'dom/string' 59 | ], 60 | 61 | xhr: [ 62 | 'xhr/xhr', 63 | 'xhr/form', 64 | 'xhr/element', 65 | 'xhr/xhr/dummy', 66 | 'xhr/xhr/iframed', 67 | 'xhr/xhr/jsonp' 68 | ], 69 | 70 | fx: [ 71 | 'fx/fx', 72 | 'fx/string', 73 | 'fx/element', 74 | 'fx/fx/morph', 75 | 'fx/fx/highlight', 76 | 'fx/fx/twin', 77 | 'fx/fx/slide', 78 | 'fx/fx/fade', 79 | 'fx/fx/attr', 80 | 'fx/fx/scroll' 81 | ], 82 | 83 | cookie: [ 84 | 'dom/cookie' 85 | ], 86 | 87 | // the old browsers support hacks 88 | olds: [ 89 | 'olds/ie', 90 | 'olds/events', 91 | 'olds/konq', 92 | 'olds/css' 93 | ] 94 | }); 95 | -------------------------------------------------------------------------------- /src/core/options.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a simple mix-in module to be included in other classes 3 | * 4 | * Basically it privdes the setOptions method which processes 5 | * an instance options assigment and merging with the default options 6 | * 7 | * Credits: 8 | * The idea of the module is inspired by 9 | * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti 10 | * 11 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 12 | */ 13 | var Options = RightJS.Options = { 14 | /** 15 | * assigns the options by merging them with the default ones 16 | * 17 | * @param Object options 18 | * @return Object current instance 19 | */ 20 | setOptions: function(opts) { 21 | var options = this.options = $ext($ext({}, 22 | Object.clone(Class_findSet(this, 'Options'))), opts 23 | ), match, key; 24 | 25 | // hooking up the observer options 26 | if (isFunction(this.on)) { 27 | for (key in options) { 28 | if ((match = key.match(/on([A-Z][A-Za-z]+)/))) { 29 | this.on(match[1].toLowerCase(), options[key]); 30 | delete(options[key]); 31 | } 32 | } 33 | } 34 | 35 | return this; 36 | }, 37 | 38 | /** 39 | * Cuts of an options hash from the end of the arguments list 40 | * assigns them using the #setOptions method and then 41 | * returns the list of other arguments as an Array instance 42 | * 43 | * @param mixed iterable 44 | * @return Array of the arguments 45 | */ 46 | cutOptions: function(in_args) { 47 | var args = $A(in_args); 48 | this.setOptions(isHash(args.last()) ? args.pop() : {}); 49 | return args; 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /src/dom/browser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this object will contain info about the current browser 3 | * 4 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 5 | */ 6 | var agent = navigator.userAgent, 7 | Browser_Opera = 'opera' in window, 8 | Browser_IE = 'attachEvent' in window && !Browser_Opera, 9 | 10 | Browser = RightJS.Browser = { 11 | IE: Browser_IE, 12 | Opera: Browser_Opera, 13 | WebKit: agent.include('AppleWebKit/'), 14 | Gecko: agent.include('Gecko') && !agent.include('KHTML'), 15 | MobileSafari: /Apple.*Mobile.*Safari/.test(agent), 16 | Konqueror: agent.include('Konqueror'), 17 | 18 | // internal marker for the browsers which require the olds module 19 | OLD: !document.querySelector, 20 | // internal marker for IE browsers version <= 8 21 | IE8L: false 22 | }, 23 | 24 | IE8_OR_LESS = false, 25 | IE_OPACITY = !('opacity' in HTML.style) && ('filter' in HTML.style); 26 | 27 | try { 28 | // checking if that an IE version <= 8 29 | document.createElement(''); 30 | Browser.OLD = Browser.IE8L = IE8_OR_LESS = true; 31 | } catch(e) {} 32 | -------------------------------------------------------------------------------- /src/dom/cookie.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this module handles the work with cookies 3 | * 4 | * Credits: 5 | * Most things in the unit are take from 6 | * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti 7 | * 8 | * Copyright (C) 2008-2010 Nikolay V. Nemshilov 9 | */ 10 | var Cookie = RightJS.Cookie = new Class({ 11 | include: Options, 12 | 13 | extend: { 14 | // sets the cookie 15 | set: function(name, value, options) { 16 | return new this(name, options).set(value); 17 | }, 18 | // gets the cookie 19 | get: function(name, options) { 20 | return new this(name, options).get(); 21 | }, 22 | // deletes the cookie 23 | remove: function(name, options) { 24 | return new this(name, options).remove(); 25 | }, 26 | 27 | // checks if the cookies are enabled 28 | enabled: function() { 29 | document.cookie = "__t=1"; 30 | return document.cookie.indexOf("__t=1")!=-1; 31 | }, 32 | 33 | // some basic options 34 | Options: { 35 | secure: false, 36 | document: document 37 | } 38 | }, 39 | 40 | /** 41 | * constructor 42 | * @param String cookie name 43 | * @param Object options 44 | * @return void 45 | */ 46 | initialize: function(name, options) { 47 | this.name = name; 48 | this.setOptions(options); 49 | }, 50 | 51 | /** 52 | * sets the cookie with the name 53 | * 54 | * @param mixed value 55 | * @return Cookie this 56 | */ 57 | set: function(data) { 58 | if (!isString(data)) { data = JSON.stringify(data); } 59 | 60 | var value = encodeURIComponent(data), options = this.options; 61 | 62 | if (options.domain) { value += '; domain=' + options.domain; } 63 | if (options.path) { value += '; path=' + options.path; } 64 | if (options.duration) { 65 | var date = new Date(); 66 | date.setTime(date.getTime() + options.duration * 24 * 60 * 60 * 1000); 67 | value += '; expires=' + date.toGMTString(); 68 | } 69 | if (options.secure) { value += '; secure'; } 70 | options.document.cookie = this.name + '=' + value; 71 | return this; 72 | }, 73 | 74 | /** 75 | * searches for a cookie with the name 76 | * 77 | * @return mixed saved value or null if nothing found 78 | */ 79 | get: function() { 80 | var value = this.options.document.cookie.match( 81 | '(?:^|;)\\s*' + RegExp.escape(this.name) + '=([^;]*)' 82 | ); 83 | if (value) { 84 | value = decodeURIComponent(value[1]); 85 | try { value = JSON.parse(value); } 86 | catch (e) {} 87 | } 88 | return value || null; 89 | }, 90 | 91 | /** 92 | * removes the cookie 93 | * 94 | * @return Cookie this 95 | */ 96 | remove: function() { 97 | this.options.duration = -1; 98 | return this.set(''); 99 | } 100 | }); 101 | -------------------------------------------------------------------------------- /src/dom/document.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A simple document wrapper 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var Document = RightJS.Document = new Class(Wrapper, { 7 | // returns the window reference 8 | win: function() { 9 | return wrap(this._.defaultView || this._.parentWindow); 10 | } 11 | }), 12 | 13 | // a common local wrapped document reference 14 | current_Document = wrap(document); 15 | -------------------------------------------------------------------------------- /src/dom/element.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The DOM Element unit handling 3 | * 4 | * Copyright (C) 2008-2011 Nikolay Nemshilov 5 | */ 6 | 7 | var Element = RightJS.Element = new Class(Wrapper, { 8 | /** 9 | * constructor 10 | * 11 | * NOTE: this constructor will dynamically typecast 12 | * the wrappers depending on the element tag-name 13 | * 14 | * @param String element tag name or an HTMLElement instance 15 | * @param Object options 16 | * @return Element element 17 | */ 18 | initialize: function(element, options) { 19 | Element_initialize(this, element, options); 20 | } 21 | 22 | }, Element_Klass), 23 | 24 | Element_wrappers = Element.Wrappers = {}, 25 | elements_cache = {}, 26 | 27 | /** 28 | * bulds dom-elements 29 | * 30 | * @param String element tag name 31 | * @param Object options 32 | * @return HTMLElement 33 | */ 34 | make_element = function (tag, options) { 35 | return (tag in elements_cache ? elements_cache[tag] : ( 36 | elements_cache[tag] = document.createElement(tag) 37 | )).cloneNode(false); 38 | }; 39 | 40 | // 41 | // IE 6,7,8 (not 9!) browsers have a bug with checkbox and radio input elements 42 | // it doesn't place the 'checked' property correctly, plus there are some issues 43 | // with clonned SELECT objects, so we are replaceing the elements maker in here 44 | // 45 | if (IE8_OR_LESS) { 46 | make_element = function(tag, options) { 47 | if (options !== undefined && (tag === 'input' || tag === 'button')) { 48 | tag = '<'+ tag +' name="'+ options.name + 49 | '" type="'+ options.type +'"'+ 50 | (options.checked ? ' checked' : '') + ' />'; 51 | 52 | delete(options.name); 53 | delete(options.type); 54 | } 55 | 56 | return document.createElement(tag); 57 | }; 58 | } 59 | 60 | /** 61 | * Basic element's constructor 62 | * 63 | * @param Element wrapper instance 64 | * @param mixed raw dom element of a string tag name 65 | * @param Object options 66 | * @return void 67 | */ 68 | function Element_initialize(inst, element, options) { 69 | if (typeof element === 'string') { 70 | inst._ = make_element(element, options); 71 | 72 | if (options !== undefined) { 73 | for (var key in options) { 74 | switch (key) { 75 | case 'id': inst._.id = options[key]; break; 76 | case 'html': inst._.innerHTML = options[key]; break; 77 | case 'class': inst._.className = options[key]; break; 78 | case 'on': inst.on(options[key]); break; 79 | default: inst.set(key, options[key]); 80 | } 81 | } 82 | } 83 | } else { 84 | inst._ = element; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/dom/element/commons.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Common DOM Element unit methods 3 | * 4 | * Credits: 5 | * Most of the naming system in the module inspired by 6 | * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson 7 | * 8 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 9 | */ 10 | Element.include({ 11 | /** 12 | * sets the element attributes 13 | * 14 | * @param String attr name or Object attributes hash 15 | * @param mixed attribute value 16 | * @return Element self 17 | */ 18 | set: function(hash, value) { 19 | if (typeof(hash) === 'string') { var val = {}; val[hash] = value; hash = val; } 20 | 21 | var key, element = this._; 22 | 23 | for (key in hash) { 24 | if (key === 'style') { 25 | this.setStyle(hash[key]); 26 | } else { 27 | // some attributes are not available as properties 28 | if (!(key in element)) { 29 | element.setAttribute(key, ''+hash[key]); 30 | } 31 | if (key.substr(0,5) !== 'data-') { 32 | element[key] = hash[key]; 33 | } 34 | } 35 | } 36 | 37 | return this; 38 | }, 39 | 40 | /** 41 | * returns the attribute value for the name 42 | * 43 | * @param String attr name 44 | * @return mixed value 45 | */ 46 | get: function(name) { 47 | var element = this._, value = element[name] || element.getAttribute(name); 48 | return value === '' ? null : value; 49 | }, 50 | 51 | /** 52 | * checks if the element has that attribute 53 | * 54 | * @param String attr name 55 | * @return Boolean check result 56 | */ 57 | has: function(name) { 58 | return this.get(name) !== null; 59 | }, 60 | 61 | /** 62 | * erases the given attribute of the element 63 | * 64 | * @param String attr name 65 | * @return Element self 66 | */ 67 | erase: function(name) { 68 | this._.removeAttribute(name); 69 | return this; 70 | }, 71 | 72 | /** 73 | * checks if the elemnt is hidden 74 | * 75 | * NOTE: will check css level computed styles too 76 | * 77 | * @return boolean check result 78 | */ 79 | hidden: function() { 80 | return this.getStyle('display') === 'none'; 81 | }, 82 | 83 | /** 84 | * checks if the element is visible 85 | * 86 | * @return boolean check result 87 | */ 88 | visible: function() { 89 | return !this.hidden(); 90 | }, 91 | 92 | /** 93 | * hides the element 94 | * 95 | * @param String optional effect name 96 | * @param Object the optional effect options 97 | * @return Element self 98 | */ 99 | hide: function(effect, options) { 100 | if (this.visible()) { 101 | this._d = this.getStyle('display'); 102 | this._.style.display = 'none'; 103 | } 104 | 105 | return this; 106 | }, 107 | 108 | /** 109 | * shows the element 110 | * 111 | * @return Element self 112 | */ 113 | show: function() { 114 | if (this.hidden()) { 115 | var element = this._, value = this._d, dummy; 116 | 117 | // trying to guess the default 'style.display' for this kind of elements 118 | if (!value || value === 'none') { 119 | dummy = $E(element.tagName).insertTo(HTML); 120 | value = dummy.getStyle('display'); 121 | dummy.remove(); 122 | } 123 | 124 | // failsafe in case the user been naughty 125 | if (value === 'none') { 126 | value = 'block'; 127 | } 128 | 129 | element.style.display = value; 130 | } 131 | 132 | return this; 133 | }, 134 | 135 | /** 136 | * toggles the visibility state of the element 137 | * 138 | * @return Element self 139 | */ 140 | toggle: function() { 141 | return this[this.visible() ? 'hide' : 'show'](); 142 | }, 143 | 144 | /** 145 | * shows the element and hides all the sibligns 146 | * 147 | * @param String optional effect name 148 | * @param Object the optional effect options 149 | * @return Element self 150 | */ 151 | radio: function(effect, options) { 152 | this.siblings().each('hide', effect, options); 153 | return this.show(); 154 | }, 155 | 156 | /** 157 | * Sets/gets the `data-smth` data attribute and 158 | * automatically converts everything in/out JSON 159 | * 160 | * @param String key name 161 | * @param mixed data or `undefined` to erase 162 | * @return mixed Element self or extracted data 163 | */ 164 | data: function(key, value) { 165 | var name, result, match, attrs, attr, i; 166 | 167 | if (isHash(key)) { 168 | for (name in key) { 169 | value = this.data(name, key[name]); 170 | } 171 | } else if (value === undefined) { 172 | key = 'data-'+ (''+key).dasherize(); 173 | 174 | for (result = {}, match = false, attrs = this._.attributes, i=0; i < attrs.length; i++) { 175 | value = attrs[i].value; 176 | try { value = JSON.parse(value); } catch (e) {} 177 | 178 | if (attrs[i].name === key) { 179 | result = value; 180 | match = true; 181 | break; 182 | } else if (attrs[i].name.indexOf(key) === 0) { 183 | result[attrs[i].name.substring(key.length+1).camelize()] = value; 184 | match = true; 185 | } 186 | } 187 | 188 | value = match ? result : null; 189 | } else { 190 | key = 'data-'+ (''+key).dasherize(); 191 | 192 | if (!isHash(value)) { value = {'': value}; } 193 | 194 | for (name in value) { 195 | attr = name.blank() ? key : key+'-'+name.dasherize(); 196 | 197 | if (value[name] === null) { 198 | this._.removeAttribute(attr); 199 | } else { 200 | this._.setAttribute(attr, isString(value[name]) ? value[name] : JSON.stringify(value[name])); 201 | } 202 | } 203 | 204 | value = this; 205 | } 206 | 207 | return value; 208 | } 209 | }); 210 | -------------------------------------------------------------------------------- /src/dom/element/dimensions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this module contains the Element's part of functionality 3 | * responsible for the dimensions and positions getting/setting 4 | * 5 | * Copyright (C) 2008-2011 Nikolay Nemshilov 6 | */ 7 | Element.include({ 8 | /** 9 | * Returns the reference to this element document 10 | * 11 | * @return RightJS.Document 12 | */ 13 | doc: function() { 14 | return wrap(this._.ownerDocument); 15 | }, 16 | 17 | /** 18 | * Returns the reference to this elements window 19 | * 20 | * @return RightJS.Window 21 | */ 22 | win: function() { 23 | return this.doc().win(); 24 | }, 25 | 26 | /** 27 | * Returns the element size as a hash 28 | * 29 | * @return Object {x: NNN, y: NNN} 30 | */ 31 | size: function() { 32 | return { x: this._.offsetWidth, y: this._.offsetHeight }; 33 | }, 34 | 35 | /** 36 | * Returns the element absolute position 37 | * 38 | * NOTE: see the konq.js file for the manual version of the method 39 | * 40 | * @return Object {x: NNN, y: NNN} 41 | */ 42 | position: function() { 43 | var rect = this._.getBoundingClientRect(), 44 | html = this.doc()._.documentElement, 45 | scrolls = this.win().scrolls(); 46 | 47 | return { 48 | x: rect.left + scrolls.x - html.clientLeft, 49 | y: rect.top + scrolls.y - html.clientTop 50 | }; 51 | }, 52 | 53 | /** 54 | * Returns the element scrolls 55 | * 56 | * @return Object {x: NNN, y: NNN} 57 | */ 58 | scrolls: function() { 59 | return { x: this._.scrollLeft, y: this._.scrollTop }; 60 | }, 61 | 62 | /** 63 | * returns the element dimensions hash 64 | * 65 | * @return Object dimensions (top, left, width, height, scrollLeft, scrollTop) 66 | */ 67 | dimensions: function() { 68 | var size = this.size(), 69 | scrolls = this.scrolls(), 70 | position = this.position(); 71 | 72 | return { 73 | top: position.y, 74 | left: position.x, 75 | width: size.x, 76 | height: size.y, 77 | scrollLeft: scrolls.x, 78 | scrollTop: scrolls.y 79 | }; 80 | }, 81 | 82 | /** 83 | * Checks if the element overlaps the given position 84 | * 85 | * @param Object position {x: NNN, y: NNN} 86 | * @return boolean check result 87 | */ 88 | overlaps: function(target) { 89 | var pos = this.position(), size = this.size(); 90 | 91 | return target.x > pos.x && target.x < (pos.x + size.x) && 92 | target.y > pos.y && target.y < (pos.y + size.y); 93 | }, 94 | 95 | /** 96 | * sets the width of the element in pixels 97 | * 98 | * NOTE: will double assign the size of the element, so it match the exact 99 | * size including any possible borders and paddings 100 | * 101 | * @param Integer width in pixels 102 | * @return Element self 103 | */ 104 | setWidth: function(width_px) { 105 | var style = this._.style; 106 | style.width = width_px + 'px'; 107 | style.width = (2 * width_px - this._.offsetWidth) + 'px'; 108 | return this; 109 | }, 110 | 111 | /** 112 | * sets the width of the element in pixels 113 | * 114 | * NOTE: will double assign the size of the element, so it match the exact 115 | * size including any possible borders and paddings 116 | * 117 | * @param Integer height in pixels 118 | * @return Element self 119 | */ 120 | setHeight: function(height_px) { 121 | var style = this._.style; 122 | style.height = height_px + 'px'; 123 | style.height = (2 * height_px - this._.offsetHeight) + 'px'; 124 | return this; 125 | }, 126 | 127 | /** 128 | * sets the size of the element in pixels 129 | * 130 | * NOTE: will double assign the size of the element, so it match the exact 131 | * size including any possible borders and paddings 132 | * 133 | * @param width Integer width in pixels or {x: 10, y: 20} like object 134 | * @param height Integer height 135 | * @return Element self 136 | */ 137 | resize: function(width, height) { 138 | if (isHash(width)) { 139 | height = width.y; 140 | width = width.x; 141 | } 142 | return this.setWidth(width).setHeight(height); 143 | }, 144 | 145 | /** 146 | * sets the element position (against the window corner) 147 | * 148 | * @param left Number left position in pixels or an object like {x: 10, y: 20} 149 | * @param top Number top position in pixels 150 | * @return Element self 151 | */ 152 | moveTo: function(left, top) { 153 | if (isHash(left)) { 154 | top = left.y; 155 | left = left.x; 156 | } 157 | 158 | return this.setStyle({ 159 | left: left + 'px', 160 | top: top + 'px' 161 | }); 162 | }, 163 | 164 | /** 165 | * sets the scroll position 166 | * 167 | * @param left Integer left scroll px or an object like {x: 22, y: 33} 168 | * @param top Integer top scroll px 169 | * @return Element self 170 | */ 171 | scrollTo: function(left, top) { 172 | if (isHash(left)) { 173 | top = left.y; 174 | left = left.x; 175 | } 176 | 177 | this._.scrollLeft = left; 178 | this._.scrollTop = top; 179 | 180 | return this; 181 | }, 182 | 183 | /** 184 | * makes the window be scrolled to the element 185 | * 186 | * @param Object fx options 187 | * @return Element self 188 | */ 189 | scrollThere: function(options) { 190 | this.win().scrollTo(this, options); 191 | return this; 192 | } 193 | }); 194 | -------------------------------------------------------------------------------- /src/dom/element/events.js: -------------------------------------------------------------------------------- 1 | /** 2 | * DOM Element events handling methods 3 | * 4 | * Copyright (C) 2008-2011 Nikolay Nemshilov 5 | */ 6 | [Element, Document, Window].each('include', $ext(Observer_create({}), { 7 | /** 8 | * The basic events handling attachment method 9 | * SEE Observer#on for more details about supported arguments 10 | * 11 | * @returnt this 12 | */ 13 | on: function() { 14 | Observer_on(this, arguments, function(hash) { 15 | 16 | if (hash.e === 'mouseenter' || hash.e === 'mouseleave') { 17 | mouse_io_activate(); 18 | hash.n = hash.e; 19 | hash.w = function() {}; 20 | // NOTE: we don't attach this listener to the actual element! 21 | // so it didn't screw with IE's native enter/leave handlers 22 | } else { 23 | if (hash.e === 'contextmenu' && Browser.Konqueror) { 24 | hash.n = 'rightclick'; 25 | } else if (hash.e === 'mousewheel' && Browser.Gecko) { 26 | hash.n = 'DOMMouseScroll'; 27 | } else { 28 | hash.n = hash.e; 29 | } 30 | 31 | hash.w = function(event) { 32 | event = new Event(event, hash.t); 33 | if (hash.f.apply(hash.t, (hash.r?[]:[event]).concat(hash.a)) === false) { 34 | event.stop(); 35 | } 36 | }; 37 | 38 | if (IE8_OR_LESS) { 39 | hash.t._.attachEvent('on'+hash.n, hash.w); 40 | } else { 41 | hash.t._.addEventListener(hash.n, hash.w, false); 42 | } 43 | } 44 | 45 | return hash; 46 | }); 47 | 48 | return this; 49 | }, 50 | 51 | /** 52 | * Stops an event handling 53 | * 54 | * @param String event name or a function callback 55 | * @param function callback or nothing 56 | * @return this 57 | */ 58 | stopObserving: function(event, callback) { 59 | Observer_stopObserving(this, event, callback, function(hash) { 60 | if (IE8_OR_LESS) { 61 | hash.t._.detachEvent('on'+ hash.n, hash.w); 62 | } else { 63 | hash.t._.removeEventListener(hash.n, hash.w, false); 64 | } 65 | }); 66 | 67 | return this; 68 | }, 69 | 70 | /** 71 | * Artificially trigers the event on the element 72 | * 73 | * @param string event name or an Event instance 74 | * @param Object options 75 | * @return this 76 | */ 77 | fire: function(event, options) { 78 | var parent = this.parent && this.parent(); 79 | 80 | if (!(event instanceof Event)) { 81 | event = new Event(event, $ext({target: this._}, options)); 82 | } 83 | 84 | // setting up the currentTarget reference 85 | event.currentTarget = this; 86 | 87 | (this.$listeners || []).each(function(hash) { 88 | if (hash.e === event.type && 89 | hash.f.apply(this, (hash.r?[]:[event]).concat(hash.a)) === false 90 | ) { 91 | event.stop(); 92 | } 93 | }, this); 94 | 95 | // manually bypassing the event to the parent one if it should bubble 96 | if (parent && parent.fire && !event.stopped) { 97 | parent.fire(event); 98 | } 99 | 100 | return this; 101 | }, 102 | 103 | /** 104 | * a simple events terminator method to be hooked like this.onClick('stopEvent'); 105 | * 106 | * @return false 107 | */ 108 | stopEvent: function() { return false; } 109 | })); 110 | 111 | // couple more shortcuts for the window 112 | Observer_createShortcuts(Window.prototype, $w('blur focus scroll resize load')); 113 | 114 | /** 115 | * Registers a list of event-binding shortcuts like 116 | * $(element).onClick 117 | * $(element).onMouseover 118 | * 119 | * @param String space separated event names 120 | * @return void 121 | */ 122 | function Element_add_event_shortcuts(tokens) { 123 | tokens = $w(tokens); 124 | Event_delegation_shortcuts = Event_delegation_shortcuts.concat(tokens); 125 | 126 | Observer_createShortcuts(Element.prototype, tokens); 127 | Observer_createShortcuts(Document.prototype, tokens); 128 | } 129 | 130 | Element_add_event_shortcuts( 131 | 'click rightclick contextmenu mousedown mouseup '+ 132 | 'mouseover mouseout mousemove keypress keydown keyup' 133 | ); 134 | -------------------------------------------------------------------------------- /src/dom/element/styles.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this module contains the element unit styles related methods 3 | * 4 | * Credits: 5 | * Some of the functionality is inspired by 6 | * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson 7 | * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti 8 | * - Dojo (www.dojotoolkit.org) Copyright (C) The Dojo Foundation 9 | * 10 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 11 | */ 12 | Element.include({ 13 | /** 14 | * assigns styles out of the hash to the element 15 | * 16 | * NOTE: the style keys might be camelized or dasherized, both cases should work 17 | * 18 | * @param Object styles list or String style name 19 | * @param String style value in case of the first param a string style name 20 | * @return Element self 21 | */ 22 | setStyle: function(hash, value) { 23 | var key, c_key, style = {}, element_style = this._.style; 24 | 25 | if (value !== undefined) { style[hash] = value; hash = style; } 26 | else if(isString(hash)) { 27 | hash.split(';').each(function(option) { 28 | var els = option.split(':').map('trim'); 29 | if (els[0] && els[1]) { 30 | style[els[0]] = els[1]; 31 | } 32 | }); 33 | hash = style; 34 | } 35 | 36 | 37 | for (key in hash) { 38 | c_key = key.indexOf('-') < 0 ? key : key.camelize(); 39 | 40 | if (IE_OPACITY && key === 'opacity') { 41 | element_style.filter = 'alpha(opacity='+ hash[key] * 100 +')'; 42 | } else if (key === 'float') { 43 | c_key = Browser_IE ? 'styleFloat' : 'cssFloat'; 44 | } 45 | 46 | element_style[c_key] = hash[key]; 47 | } 48 | 49 | return this; 50 | }, 51 | 52 | /** 53 | * returns style of the element 54 | * 55 | * NOTE: will include the CSS level definitions 56 | * 57 | * @param String style key 58 | * @return String style value or null if not set 59 | */ 60 | getStyle: function(key) { 61 | return clean_style(this._.style, key) || clean_style(this.computedStyles(), key); 62 | }, 63 | 64 | /** 65 | * returns the hash of computed styles for the element 66 | * 67 | * @return Object/CSSDefinition computed styles 68 | */ 69 | computedStyles: HTML.currentStyle ? function() { 70 | return this._.currentStyle || {}; 71 | } : HTML.runtimeStyle ? function() { 72 | return this._.runtimeStyle || {}; 73 | } : function() { 74 | return this._.ownerDocument.defaultView.getComputedStyle(this._, null); 75 | }, 76 | 77 | /** 78 | * checks if the element has the given class name 79 | * 80 | * @param String class name 81 | * @return boolean check result 82 | */ 83 | hasClass: function(name) { 84 | return (' '+this._.className+' ').indexOf(' '+name+' ') != -1; 85 | }, 86 | 87 | /** 88 | * sets the whole class-name string for the element 89 | * 90 | * @param String class-name 91 | * @return Element self 92 | */ 93 | setClass: function(class_name) { 94 | this._.className = class_name; 95 | return this; 96 | }, 97 | 98 | /** 99 | * Returns the current class-name 100 | * 101 | * @return String class-name 102 | */ 103 | getClass: function() { 104 | return this._.className; 105 | }, 106 | 107 | /** 108 | * adds the given class name to the element 109 | * 110 | * @param String class name 111 | * @return Element self 112 | */ 113 | addClass: function(name) { 114 | var testee = ' '+this._.className+' '; 115 | if (testee.indexOf(' '+name+' ') == -1) { 116 | this._.className += (testee === ' ' ? '' : ' ') + name; 117 | } 118 | return this; 119 | }, 120 | 121 | /** 122 | * removes the given class name 123 | * 124 | * @param String class name 125 | * @return Element self 126 | */ 127 | removeClass: function(name) { 128 | this._.className = (' '+this._.className+' ').replace(' '+name+' ', ' ').trim(); 129 | return this; 130 | }, 131 | 132 | /** 133 | * toggles the given class name on the element 134 | * 135 | * @param String class name 136 | * @return Element self 137 | */ 138 | toggleClass: function(name) { 139 | return this[this.hasClass(name) ? 'removeClass' : 'addClass'](name); 140 | }, 141 | 142 | /** 143 | * adds the given class-name to the element 144 | * and removes it from all the element siblings 145 | * 146 | * @param String class name 147 | * @return Element self 148 | */ 149 | radioClass: function(name) { 150 | this.siblings().each('removeClass', name); 151 | return this.addClass(name); 152 | } 153 | }); 154 | 155 | /** 156 | * cleans up a style value 157 | * 158 | * @param Object styles hash 159 | * @param String style-key 160 | * @return String clean style 161 | */ 162 | function clean_style(style, key) { 163 | key = key.camelize(); 164 | 165 | if (key === 'opacity') { 166 | return IE_OPACITY ? ( 167 | (/opacity=(\d+)/i.exec(style.filter || '') || 168 | ['', '100'])[1].toInt() / 100 169 | )+'' :style[key].replace(',', '.'); 170 | } 171 | 172 | if (key === 'float') { 173 | key = Browser_IE ? 'styleFloat' : 'cssFloat'; 174 | } 175 | 176 | var value = style[key]; 177 | 178 | // Opera returns named colors with quotes 179 | if (Browser_Opera && /color/i.test(key) && value) { 180 | value = value.replace(/"/g, ''); 181 | } 182 | 183 | return value; 184 | } 185 | -------------------------------------------------------------------------------- /src/dom/event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * represents some additional functionality for the Event class 3 | * 4 | * NOTE: there more additional functionality for the Event class in the rightjs-goods project 5 | * 6 | * Credits: 7 | * The additional method names are inspired by 8 | * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson 9 | * 10 | * Copyright (C) 2008-2011 Nikolay Nemshilov 11 | */ 12 | var Event = RightJS.Event = new Class(Wrapper, { 13 | // predefining the keys to spped up the assignments 14 | type: null, 15 | 16 | which: null, 17 | keyCode: null, 18 | 19 | target: null, 20 | currentTarget: null, 21 | relatedTarget: null, 22 | 23 | pageX: null, 24 | pageY: null, 25 | 26 | /** 27 | * the class constructor 28 | * 29 | * @param raw dom-event 30 | * @param HTMLElement the bound element 31 | * @return void 32 | */ 33 | initialize: Event_Klass, // the actual initialization happens in the Klass function 34 | 35 | /** 36 | * Stops the event bubbling process 37 | * 38 | * @return RightJS.Event this 39 | */ 40 | stopPropagation: function() { 41 | if (this._.stopPropagation) { 42 | this._.stopPropagation(); 43 | } else { 44 | this._.cancelBubble = true; 45 | } 46 | 47 | this.stopped = true; 48 | return this; 49 | }, 50 | 51 | /** 52 | * Prevents the default browser action on the event 53 | * 54 | * @return RightJS.Event this 55 | */ 56 | preventDefault: function() { 57 | if (this._.preventDefault) { 58 | this._.preventDefault(); 59 | } else { 60 | this._.returnValue = false; 61 | } 62 | 63 | return this; 64 | }, 65 | 66 | /** 67 | * Fully stops the event 68 | * 69 | * @return RightJS.Event this 70 | */ 71 | stop: function() { 72 | return this.stopPropagation().preventDefault(); 73 | }, 74 | 75 | /** 76 | * Returns the event position 77 | * 78 | * @return Object {x: ..., y: ...} 79 | */ 80 | position: function() { 81 | return {x: this.pageX, y: this.pageY}; 82 | }, 83 | 84 | /** 85 | * Returns the event's offset relative to the target element 86 | * 87 | * @return Object {x: ..., y: ...} or null 88 | */ 89 | offset: function() { 90 | if(this.target instanceof Element) { 91 | var element_position = this.target.position(); 92 | 93 | return { 94 | x: this.pageX - element_position.x, 95 | y: this.pageY - element_position.y 96 | }; 97 | } 98 | 99 | // triggered outside browser window (at toolbar etc.) 100 | return null; 101 | }, 102 | 103 | /** 104 | * Finds the element between the event target 105 | * and the boundary element that matches the 106 | * css-rule 107 | * 108 | * @param String css-rule 109 | * @return Element element or null 110 | */ 111 | find: function(css_rule) { 112 | if (this.target instanceof Wrapper && this.currentTarget instanceof Wrapper) { 113 | var target = this.target._, 114 | search = this.currentTarget.find(css_rule, true); 115 | 116 | while (target) { 117 | if (search.indexOf(target) !== -1) { 118 | return wrap(target); 119 | } 120 | target = target.parentNode; 121 | } 122 | } 123 | 124 | return undefined; 125 | } 126 | }, Event_Klass), 127 | 128 | Event_delegation_shortcuts = []; 129 | -------------------------------------------------------------------------------- /src/dom/event/focusin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module provides correct focus/blur events bubbling 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | 7 | /** 8 | * Triggers a manual focus/blur events bubbling 9 | * 10 | * @param raw dom-event 11 | * @return void 12 | */ 13 | function focus_boobler(raw_event) { 14 | var event = new Event(raw_event), 15 | target = event.target, 16 | parent = target.parent && target.parent(); 17 | 18 | event.type = raw_event.type === 'focusin' || raw_event.type === 'focus' ? 'focus' : 'blur'; 19 | 20 | if (parent) { parent.fire(event); } 21 | } 22 | 23 | /** 24 | * Hooking up the 'focus' and 'blur' events 25 | * at the document level and then rebooble them 26 | * manually like they were normal events 27 | * 28 | */ 29 | if (IE8_OR_LESS) { 30 | document.attachEvent('onfocusin', focus_boobler); 31 | document.attachEvent('onfocusout', focus_boobler); 32 | } else { 33 | document.addEventListener('focus', focus_boobler, true); 34 | document.addEventListener('blur', focus_boobler, true); 35 | } 36 | -------------------------------------------------------------------------------- /src/dom/event/mouseio.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides the mouse enter/leave events handling emulation 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var mouse_io_index = [], mouse_io_inactive = true; 7 | 8 | /** 9 | * Fires the actual mouseenter/mouseleave event 10 | * 11 | * @param original event 12 | * @param raw dom element 13 | * @param integer uid 14 | * @param boolean mouseenter or mouseleave 15 | * @return void 16 | */ 17 | function mouse_io_fire(raw, element, uid, enter) { 18 | var event = new Event(raw); 19 | event.type = enter === true ? 'mouseenter' : 'mouseleave'; 20 | event.bubbles = false; 21 | event.stopped = true; 22 | event.target = wrap(element); 23 | 24 | // replacing the #find method so that UJS didn't 25 | // get broke with trying to find nested elements 26 | event.find = function(css_rule) { 27 | return $$(css_rule, true) 28 | .indexOf(this.target._) === -1 ? 29 | undefined : this.target; 30 | }; 31 | 32 | event.target.fire(event); 33 | current_Document.fire(event); 34 | } 35 | 36 | /** 37 | * Figures out the enter/leave events by listening the 38 | * mouseovers in the document 39 | * 40 | * @param raw dom event 41 | * @return void 42 | */ 43 | function mouse_io_handler(e) { 44 | var target = e.target || e.srcElement, 45 | from = e.relatedTarget || e.fromElement, 46 | element = target, 47 | passed = false, 48 | parents = [], 49 | uid, event; 50 | 51 | while (element.nodeType === 1) { 52 | uid = $uid(element); 53 | 54 | if (mouse_io_index[uid] === undefined) { 55 | mouse_io_fire(e, element, uid, 56 | mouse_io_index[uid] = true 57 | ); 58 | } 59 | 60 | if (element === from) { 61 | passed = true; 62 | } 63 | 64 | parents.push(element); 65 | 66 | element = element.parentNode; 67 | } 68 | 69 | if (from && !passed) { 70 | while (from !== null && from.nodeType === 1 && parents.indexOf(from) === -1) { 71 | uid = $uid(from); 72 | if (mouse_io_index[uid] !== undefined) { 73 | mouse_io_fire(e, from, uid, 74 | mouse_io_index[uid] = undefined 75 | ); 76 | } 77 | 78 | from = from.parentNode; 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * Calling 'mouseleave' for all currently active elements on the page 85 | * 86 | * @return void 87 | */ 88 | function mouse_io_reset(e) { 89 | mouse_io_index.each(function(value, uid) { 90 | if (value && Wrappers_Cache[uid]) { 91 | mouse_io_fire(e, Wrappers_Cache[uid]._, uid, false); 92 | } 93 | }); 94 | } 95 | 96 | /** 97 | * Activating the mouse-io events emulation 98 | * 99 | * @return void 100 | */ 101 | function mouse_io_activate() { 102 | if (mouse_io_inactive) { 103 | mouse_io_inactive = false; 104 | 105 | if (Browser_IE) { 106 | document.attachEvent('onmouseover', mouse_io_handler); 107 | window.attachEvent('blur', mouse_io_reset); 108 | } else { 109 | document.addEventListener('mouseover', mouse_io_handler, false); 110 | window.addEventListener('blur', mouse_io_reset, false); 111 | } 112 | } 113 | } 114 | 115 | Element_add_event_shortcuts('mouseenter mouseleave'); -------------------------------------------------------------------------------- /src/dom/form.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The form unit class and extensions 3 | * 4 | * Credits: 5 | * The basic principles of the module are inspired by 6 | * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson 7 | * 8 | * Copyright (C) 2009-2011 Nikolay Nemshilov 9 | */ 10 | 11 | var Form = RightJS.Form = Element_wrappers.FORM = new Class(Element, { 12 | /** 13 | * constructor 14 | * 15 | * NOTE: this constructor can be called as a normal Element constructor 16 | * or with the options only, which will make a FORM element 17 | * 18 | * var form = new Form(raw_form_object_element); 19 | * var form = new Form({method: 'post', action: '/boo/hoo'}); 20 | * 21 | * @param Object options or HTMLFormElement object 22 | * @return void 23 | */ 24 | initialize: function(in_options) { 25 | var options = in_options || {}, remote = 'remote' in options, element = options; 26 | 27 | if (isHash(options) && !isElement(options)) { 28 | element = 'form'; 29 | options = Object.without(options, 'remote'); 30 | } 31 | 32 | this.$super(element, options); 33 | 34 | if (remote) { 35 | this.remotize(); 36 | } 37 | }, 38 | 39 | /** 40 | * returns the form elements as an array of extended units 41 | * 42 | * @return Array of elements 43 | */ 44 | elements: function() { 45 | return this.find('input,button,select,textarea'); 46 | }, 47 | 48 | /** 49 | * returns the list of all the input elements on the form 50 | * 51 | * @return Array of elements 52 | */ 53 | inputs: function() { 54 | return this.elements().filter(function(input) { 55 | return !['submit', 'button', 'reset', 'image', null].include(input._.type); 56 | }); 57 | }, 58 | 59 | /** 60 | * Accessing an input by name 61 | * 62 | * @param String name 63 | * @return Input field 64 | */ 65 | input: function(name) { 66 | var input = this._[name]; 67 | 68 | if ('tagName' in input) { 69 | input = wrap(input); 70 | } else { // a list of radio-buttons (coz they have all the same name) 71 | input = $A(input).map(wrap); 72 | } 73 | 74 | return input; 75 | }, 76 | 77 | /** 78 | * focuses on the first input element on the form 79 | * 80 | * @return Form this 81 | */ 82 | focus: function() { 83 | var element = this.inputs().first(function(input) { 84 | return input._.type !== 'hidden'; 85 | }); 86 | 87 | if (element) { element.focus(); } 88 | 89 | return this; 90 | }, 91 | 92 | /** 93 | * removes focus out of all the form elements 94 | * 95 | * @return Form this 96 | */ 97 | blur: function() { 98 | this.elements().each('blur'); 99 | return this; 100 | }, 101 | 102 | /** 103 | * disables all the elements on the form 104 | * 105 | * @return Form this 106 | */ 107 | disable: function() { 108 | this.elements().each('disable'); 109 | return this; 110 | }, 111 | 112 | /** 113 | * enables all the elements on the form 114 | * 115 | * @return Form this 116 | */ 117 | enable: function() { 118 | this.elements().each('enable'); 119 | return this; 120 | }, 121 | 122 | /** 123 | * returns the list of the form values 124 | * 125 | * @return Object values 126 | */ 127 | values: function() { 128 | var values = {}; 129 | 130 | this.inputs().each(function (element) { 131 | var input = element._, 132 | hash = values, key, 133 | keys = input.name.match(/[^\[]+/g); 134 | 135 | if (!input.disabled && input.name && (!(input.type === 'checkbox' || input.type === 'radio') || input.checked)) { 136 | // getting throught the smth[smth][smth][] in the name 137 | while (keys.length > 1) { 138 | key = keys.shift(); 139 | if (key.endsWith(']')) { 140 | key = key.substr(0, key.length-1); 141 | } 142 | if (!hash[key]) { 143 | hash[key] = keys[0] === ']' ? [] : {}; 144 | } 145 | hash = hash[key]; 146 | } 147 | 148 | key = keys.shift(); 149 | if (key.endsWith(']')) { 150 | key = key.substr(0, key.length-1); 151 | } 152 | 153 | if (key === '') { // an array 154 | hash.push(element.value()); 155 | } else { 156 | hash[key] = element.value(); 157 | } 158 | } 159 | }); 160 | 161 | return values; 162 | }, 163 | 164 | /** 165 | * returns the key/values organized ready to be sent via a get request 166 | * 167 | * @return String serialized values 168 | */ 169 | serialize: function() { 170 | return Object.toQueryString(this.values()); 171 | }, 172 | 173 | /** 174 | * Delegating the submit method 175 | * 176 | * @return Form this 177 | */ 178 | submit: function() { 179 | this._.submit(); 180 | return this; 181 | }, 182 | 183 | /** 184 | * Delegating the 'reset' method 185 | * 186 | * @return Form this 187 | */ 188 | reset: function() { 189 | this._.reset(); 190 | return this; 191 | } 192 | }); 193 | 194 | // creating the event shortcuts 195 | Element_add_event_shortcuts('submit reset focus blur disable enable change'); 196 | -------------------------------------------------------------------------------- /src/dom/input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The form input element class 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | var Input = RightJS.Input = 7 | 8 | // retgistering the typecasted wrappers 9 | Element_wrappers.INPUT = 10 | Element_wrappers.BUTTON = 11 | Element_wrappers.SELECT = 12 | Element_wrappers.TEXTAREA = 13 | Element_wrappers.OPTGROUP = 14 | 15 | new Class(Element, { 16 | /** 17 | * Constructor 18 | * 19 | * NOTE: this constructor can be called in several ways 20 | * 21 | * Like normal Element 22 | * var input = new Input('texarea', {...}); 23 | * var input = new Input(document.createElement('select')); 24 | * 25 | * Or with options only which will make an INPUT element by default 26 | * var input = new Input({type: 'password', name: 'password'}); 27 | * 28 | * @param HTMLElement or a String tag name or Options for default 'input' tag 29 | * @param Object options 30 | * @return void 31 | */ 32 | initialize: function(element, options) { 33 | // type to tag name conversion 34 | if (!element || (isHash(element) && !isElement(element))) { 35 | options = element || {}; 36 | 37 | if (/textarea|select/.test(options.type || '')) { 38 | element = options.type; 39 | delete(options.type); 40 | } else { 41 | element = 'input'; 42 | } 43 | } 44 | 45 | this.$super(element, options); 46 | }, 47 | 48 | /** 49 | * Returns a reference to the input's form 50 | * 51 | * @return Form wrapped form 52 | */ 53 | form: function() { 54 | return wrap(this._.form); 55 | }, 56 | 57 | /** 58 | * Overloading the method to fix some issues with IE and FF 59 | * 60 | * @param mixed content 61 | * @param string optional position 62 | * @return Input this 63 | */ 64 | insert: function(content, position) { 65 | this.$super(content, position); 66 | 67 | // manually resetting the selected option in here 68 | this.find('option').each(function(option) { 69 | option._.selected = !!option.get('selected'); 70 | }); 71 | 72 | return this; 73 | }, 74 | 75 | /** 76 | * Overloading the method so it always called the '#insert' method 77 | * 78 | * @param mixed content 79 | * @return Input this 80 | */ 81 | update: function(content) { 82 | return this.clean().insert(content); 83 | }, 84 | 85 | /** 86 | * uniform access to the element values 87 | * 88 | * @return String element value 89 | */ 90 | getValue: function() { 91 | if (this._.type == 'select-multiple') { 92 | return this.find('option').map(function(option) { 93 | return option._.selected ? option._.value : null; 94 | }).compact(); 95 | } else { 96 | return this._.value; 97 | } 98 | }, 99 | 100 | /** 101 | * uniform accesss to set the element value 102 | * 103 | * @param String value 104 | * @return Element this 105 | */ 106 | setValue: function(value) { 107 | if (this._.type == 'select-multiple') { 108 | value = ensure_array(value).map(String); 109 | this.find('option').each(function(option) { 110 | option._.selected = value.include(option._.value); 111 | }); 112 | } else { 113 | this._.value = value; 114 | } 115 | return this; 116 | }, 117 | 118 | /** 119 | * Both ways getter/setter for the value parameter 120 | * 121 | * @param mixed value 122 | * @return mixed this or the value 123 | */ 124 | value: function(value) { 125 | return this[value === undefined ? 'getValue' : 'setValue'](value); 126 | }, 127 | 128 | /** 129 | * focuses on the first input element on the form 130 | * 131 | * @return Form this 132 | */ 133 | focus: function() { 134 | this._.focus(); 135 | this.focused = true; 136 | if (Browser_IE) { this.fire('focus', {bubbles: false}); } 137 | return this; 138 | }, 139 | 140 | /** 141 | * removes focus out of all the form elements 142 | * 143 | * @return Form this 144 | */ 145 | blur: function() { 146 | this._.blur(); 147 | this.focused = false; 148 | if (Browser_IE) { this.fire('blur', {bubbles: false}); } 149 | return this; 150 | }, 151 | 152 | /** 153 | * focuses on the element and selects its content 154 | * 155 | * @return Element this 156 | */ 157 | select: function() { 158 | this._.select(); 159 | return this.focus(); 160 | }, 161 | 162 | /** 163 | * disables all the elements on the form 164 | * 165 | * @return Form this 166 | */ 167 | disable: function() { 168 | this._.disabled = true; 169 | return this.fire('disable'); 170 | }, 171 | 172 | /** 173 | * enables all the elements on the form 174 | * 175 | * @return Form this 176 | */ 177 | enable: function() { 178 | this._.disabled = false; 179 | return this.fire('enable'); 180 | }, 181 | 182 | /** 183 | * A bidirectional method to set/get the disabled status of the input field 184 | * 185 | * @param boolean optional value 186 | * @return Input in setter mode boolean in getter 187 | */ 188 | disabled: function(value) { 189 | return value === undefined ? this._.disabled : this[value ? 'disable' : 'enable'](); 190 | }, 191 | 192 | /** 193 | * A bidirectional method to set/get the checked status of the input field 194 | * 195 | * @param boolean optional value 196 | * @return Input in setter mode boolean in getter 197 | */ 198 | checked: function(value) { 199 | if (value === undefined) { 200 | value = this._.checked; 201 | } else { 202 | this._.checked = value; 203 | value = this; 204 | } 205 | 206 | return value; 207 | } 208 | }); 209 | -------------------------------------------------------------------------------- /src/dom/ready.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The dom-ready event handling code 3 | * 4 | * Credits: 5 | * The basic principles of the module are originated from 6 | * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti 7 | * 8 | * Copyright (C) 2009-2011 Nikolay Nemshilov 9 | */ 10 | Document.include({ 11 | on: function(name) { 12 | if (name === 'ready' && !this._iR) { 13 | var document = this._, ready = this.fire.bind(this, 'ready'); 14 | 15 | // IE and Konqueror browsers 16 | if ('readyState' in document) { 17 | (function() { 18 | if (['loaded','complete'].include(document.readyState)) { 19 | ready(); 20 | } else { 21 | arguments.callee.delay(50); 22 | } 23 | })(); 24 | } else { 25 | document.addEventListener('DOMContentLoaded', ready, false); 26 | } 27 | 28 | this._iR = true; 29 | } 30 | 31 | return this.$super.apply(this, arguments); 32 | } 33 | }); 34 | 35 | Observer_createShortcuts(Document.prototype, ['ready']); -------------------------------------------------------------------------------- /src/dom/selector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The DOM elements selection handling 3 | * 4 | * NOTE: this module is just a wrap over the native CSS-selectors feature 5 | * see the olds/css.js file for the manual selector code 6 | * 7 | * Copyright (C) 2008-2011 Nikolay Nemshilov 8 | */ 9 | 10 | [Element, Document].each('include', { 11 | /** 12 | * Extracts the first element matching the css-rule, 13 | * or just any first element if no css-rule was specified 14 | * 15 | * @param String css-rule 16 | * @return Element matching node or null 17 | */ 18 | first: function(css_rule) { 19 | return wrap( 20 | css_rule === undefined && this._.firstElementChild !== undefined ? 21 | this._.firstElementChild : this._.querySelector(css_rule || '*') 22 | ); 23 | }, 24 | 25 | /** 26 | * Finds a list of matching nodes, or all the descendant nodes if no css-rule provided 27 | * 28 | * @param String css-rule 29 | * @param boolean raw-search 30 | * @return Array of elements 31 | */ 32 | find: function(css_rule, raw) { 33 | var query = this._.querySelectorAll(css_rule || '*'), result, i=0, l = query.length; 34 | 35 | if (raw === true) { 36 | result = $A(query); 37 | } else { 38 | for (result = []; i < l; i++) { 39 | result[i] = wrap(query[i]); 40 | } 41 | } 42 | 43 | return result; 44 | }, 45 | 46 | /** 47 | * checks if the element matches this css-rule 48 | * 49 | * NOTE: the element should be attached to the page 50 | * 51 | * @param String css-rule 52 | * @return Boolean check result 53 | */ 54 | match: function(css_rule) { 55 | // finding the top parent element (the element might not be on the document) 56 | var element = this._, parent = element, result, faking = false; 57 | 58 | while (parent.parentNode !== null && parent.parentNode.nodeType !== 11) { 59 | parent = parent.parentNode; 60 | } 61 | 62 | // creating a fake context when needed 63 | if (element === parent) { 64 | parent = document.createElement('div'); 65 | parent.appendChild(element); 66 | faking = true; 67 | } 68 | 69 | result = wrap(parent).find(css_rule, true).indexOf(element) !== -1; 70 | 71 | if (faking) { 72 | parent.removeChild(element); 73 | } 74 | 75 | return result; 76 | } 77 | }); 78 | -------------------------------------------------------------------------------- /src/dom/string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Some String level shortcuts to handle collections of elements 3 | * 4 | * Copyright (C) 2011 Nikolay Nemshilov 5 | */ 6 | 7 | /** 8 | * Some nice shortcuts for the document-level events delegation handling 9 | * 10 | * USAGE: 11 | * 12 | * "ul#main-menu li".on("click", function() { alert('clicked'); }); 13 | * "ul#main-menu li".on("mouseover", "addClass", "hovered"); 14 | * "ul#main-menu li".on("mouseout", "removeClass", "hovered"); 15 | * 16 | * // or like that in a shash 17 | * "ul#main-menu li".on({ 18 | * click: function() { alert('clicked'); }, 19 | * mouseover: ['addClass', 'hovered'], 20 | * mouseout: ['removeClass', 'hovered'], 21 | * dblclick: 'hide' 22 | * }); 23 | * 24 | * 25 | * "#css.rule".observes('click'); 26 | * "#css.rule".observes('click', function() {}); 27 | * "#css.rule".observes('click', 'method_name'); 28 | * .... 29 | * 30 | * "#css.rule".stopObserving('click'); 31 | * "#css.rule".stopObserving('click', function() {}); 32 | * "#css.rule".stopObserving('click', 'method_name'); 33 | * .... 34 | */ 35 | Object.each({ 36 | on: 'delegate', 37 | stopObserving: 'undelegate', 38 | observes: 'delegates' 39 | }, function(name, method) { 40 | String.prototype[name] = function() { 41 | var args = $A(arguments), result; 42 | 43 | args.splice(1,0,''+this); 44 | result = current_Document[method].apply(current_Document, args); 45 | 46 | return result === current_Document ? this : result; 47 | }; 48 | }); 49 | var old_on = String.prototype.on; 50 | String.prototype.on = function(hash) { 51 | if (isHash(hash)) { 52 | for (var key in hash) { 53 | old_on.apply(this, [key].concat([hash[key]])); 54 | } 55 | } else { 56 | old_on.apply(this, arguments); 57 | } 58 | return this; 59 | }; 60 | 61 | /** 62 | * building the list of String#onEvent shortucts 63 | * 64 | * USAGE: 65 | * 66 | * "#css.rule".onClick(function() {...}); 67 | * "#css.rule".onMouseover('method_name'); 68 | */ 69 | Event_delegation_shortcuts.each(function(name) { 70 | String.prototype['on'+name.capitalize()] = function() { 71 | return this.on.apply(this, [name].concat($A(arguments))); 72 | }; 73 | }); 74 | 75 | /** 76 | * The rest of the DOM methods access 77 | * 78 | * USAGE: 79 | * "#css.rule".addClass('boo-hoo'); 80 | * "#css.rule".setStyle({color: 'red'}); 81 | * 82 | */ 83 | $w('Element Input Form').each(function(klass) { 84 | Object.each(klass in RightJS ? RightJS[klass].prototype : {}, function(name, method) { 85 | if (isFunction(method) && !(name in String.prototype)) { 86 | String.prototype[name] = function() { 87 | var nodes = $$(this, true), i=0, l = nodes.length, first=true, element, result; 88 | for (; i < l; i++) { 89 | element = wrap(nodes[i]); 90 | result = element[name].apply(element, arguments); 91 | 92 | // checking if that's a data-retrieving call 93 | if (first) { 94 | if (result !== element) { 95 | return result; 96 | } 97 | first = false; 98 | } 99 | } 100 | 101 | // don't return the string itself in here, 102 | // it will screw with data-retrieving calls on empty collections 103 | return null; 104 | }; 105 | } 106 | }); 107 | }); -------------------------------------------------------------------------------- /src/dom/window.js: -------------------------------------------------------------------------------- 1 | /** 2 | * the window object extensions 3 | * 4 | * Copyright (C) 2008-2011 Nikolay Nemshilov 5 | */ 6 | var Window = RightJS.Window = new Class(Wrapper, { 7 | /** 8 | * Selfreference to have a common interface with the rest of the wrappers 9 | * in case of events handling 10 | * 11 | * @return Window 12 | */ 13 | win: function() { 14 | return this; 15 | }, 16 | 17 | /** 18 | * returns the inner-size of the window 19 | * 20 | * @return Object x: d+, y: d+ 21 | */ 22 | size: function() { 23 | var win = this._, html = win.document.documentElement; 24 | return win.innerWidth ? {x: win.innerWidth, y: win.innerHeight} : 25 | {x: html.clientWidth, y: html.clientHeight}; 26 | }, 27 | 28 | /** 29 | * returns the scrolls for the window 30 | * 31 | * @return Object x: d+, y: d+ 32 | */ 33 | scrolls: function() { 34 | var win = this._, doc = win.document, body = doc.body, html = doc.documentElement; 35 | 36 | return (win.pageXOffset || win.pageYOffset) ? {x: win.pageXOffset, y: win.pageYOffset} : 37 | (body && (body.scrollLeft || body.scrollTop)) ? {x: body.scrollLeft, y: body.scrollTop} : 38 | {x: html.scrollLeft, y: html.scrollTop}; 39 | }, 40 | 41 | /** 42 | * overloading the native scrollTo method to support hashes and element references 43 | * 44 | * @param mixed number left position, a hash position, element or a string element id 45 | * @param number top position 46 | * @param Object fx options 47 | * @return window self 48 | */ 49 | scrollTo: function(left, top, fx_options) { 50 | var left_pos = left, top_pos = top, 51 | element = isNumber(left) ? null : $(left); 52 | 53 | if(element instanceof Element) { 54 | left = element.position(); 55 | } 56 | 57 | if (isHash(left)) { 58 | top_pos = left.y; 59 | left_pos = left.x; 60 | } 61 | 62 | // checking if a smooth scroll was requested 63 | if (isHash(fx_options = fx_options || top) && RightJS.Fx) { 64 | new Fx.Scroll(this, fx_options).start({x: left_pos, y: top_pos}); 65 | } else { 66 | this._.scrollTo(left_pos, top_pos); 67 | } 68 | 69 | return this; 70 | } 71 | }); 72 | -------------------------------------------------------------------------------- /src/dom/wrapper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The dom-wrapper main unit 3 | * 4 | * This unit is basically for the internal use 5 | * so that we could control the common functionality 6 | * among all the wrappers 7 | * 8 | * Copyright (C) 2010-2011 Nikolay Nemshilov 9 | */ 10 | 11 | var Wrapper = RightJS.Wrapper = new Class({ 12 | // predefining the property in the prototype 13 | _: undefined, 14 | 15 | /** 16 | * Default constructor 17 | * 18 | * @param mixed raw dom unit 19 | * @return void 20 | */ 21 | initialize: function(raw_object) { 22 | this._ = raw_object; 23 | } 24 | }); 25 | 26 | // exposing the cache so it could be manupulated externally 27 | Wrapper.Cache = Wrappers_Cache; 28 | 29 | // instantiating the actual class object for a wrapper 30 | function Wrapper_makeKlass() { 31 | /** 32 | * Default wrappers Klass function 33 | * 34 | * @param mixed the raw object 35 | * @param Object options 36 | * @return void 37 | */ 38 | return function(object, options) { 39 | Class_checkPrebind(this); 40 | 41 | this.initialize.apply(this, arguments); // <- there might be a different number of args in a subclass 42 | 43 | var item = this._, uid = UID_KEY in item ? item[UID_KEY] : 44 | // NOTE we use positive indexes for dom-elements and negative for everything else 45 | (item[UID_KEY] = (item.nodeType === 1 ? 1 : -1) * UID++); 46 | 47 | Wrappers_Cache[uid] = this; 48 | }; 49 | } 50 | 51 | /** 52 | * Element's own Klass function 53 | * we need that because it does some dynamic typecasting mumbo jumbo 54 | * plus we would like to optimize some stuff here and there 55 | * 56 | * @param raw dom element or the tag name 57 | * @param Object options 58 | * @return Element instance 59 | */ 60 | function Element_Klass(element, options) { 61 | Element_initialize(this, element, options); 62 | 63 | var inst = this, raw = inst._, cast = Wrapper.Cast(raw), 64 | uid = UID_KEY in raw ? raw[UID_KEY] : (raw[UID_KEY] = UID++); 65 | 66 | if (cast !== undefined) { 67 | inst = new cast(raw, options); 68 | if ('$listeners' in this) { 69 | inst.$listeners = this.$listeners; 70 | } 71 | } 72 | 73 | Wrappers_Cache[uid] = inst; 74 | 75 | return inst; 76 | } 77 | 78 | // searches for a suitable class for dynamic typecasting 79 | Wrapper.Cast = function(unit) { 80 | return unit.tagName in Element_wrappers ? Element_wrappers[unit.tagName] : undefined; 81 | }; 82 | 83 | /** 84 | * Event's own Klass function, we don't need to check 85 | * nothing in here, don't need to hit the wrappers cache and so one 86 | * 87 | * @param raw dom-event or a string event-name 88 | * @param bounding element or an object with options 89 | * @return void 90 | */ 91 | function Event_Klass(event, bound_element) { 92 | if (typeof(event) === 'string') { 93 | event = $ext({type: event}, bound_element); 94 | this.stopped = event.bubbles === false; 95 | 96 | if (isHash(bound_element)) { 97 | $ext(this, bound_element); 98 | } 99 | } 100 | 101 | this._ = event; 102 | this.type = event.type; 103 | 104 | this.which = event.which; 105 | this.keyCode = event.keyCode; 106 | 107 | this.target = wrap( 108 | // Webkit throws events on textual nodes as well, gotta fix that 109 | event.target != null && 'nodeType' in event.target && event.target.nodeType === 3 ? 110 | event.target.parentNode : event.target 111 | ); 112 | 113 | this.currentTarget = wrap(event.currentTarget); 114 | this.relatedTarget = wrap(event.relatedTarget); 115 | 116 | this.pageX = event.pageX; 117 | this.pageY = event.pageY; 118 | 119 | // making old IE attrs looks like w3c standards 120 | if (IE8_OR_LESS && 'srcElement' in event) { 121 | this.which = event.button === 2 ? 3 : event.button === 4 ? 2 : 1; 122 | 123 | this.target = wrap(event.srcElement) || bound_element; 124 | this.relatedTarget = this.target._ === event.fromElement ? wrap(event.toElement) : this.target; 125 | this.currentTarget = bound_element; 126 | 127 | var scrolls = this.target.win().scrolls(); 128 | 129 | this.pageX = event.clientX + scrolls.x; 130 | this.pageY = event.clientY + scrolls.y; 131 | } 132 | } 133 | 134 | 135 | /** 136 | * Private quick wrapping function, unlike `$` 137 | * it doesn't search by ID and handle double-wrapps 138 | * just pure dom-wrapping functionality 139 | * 140 | * @param raw dom unit 141 | * @return Wrapper dom-wrapper 142 | */ 143 | function wrap(object) { 144 | if (object != null) { 145 | var wrapper = UID_KEY in object ? Wrappers_Cache[object[UID_KEY]] : undefined; 146 | 147 | if (wrapper !== undefined) { 148 | return wrapper; 149 | } else if (object.nodeType === 1) { 150 | return new Element(object); 151 | } else if (object.nodeType === 9) { 152 | return new Document(object); 153 | } else if (object.window == object) { 154 | return new Window(object); 155 | } else if (isElement(object.target) || isElement(object.srcElement)) { 156 | return new Event(object); 157 | } 158 | } 159 | 160 | return object; 161 | } -------------------------------------------------------------------------------- /src/fx/element.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This block contains additional Element shortcuts for effects easy handling 3 | * 4 | * Credits: 5 | * Some ideas are inspired by 6 | * - MooTools (http://mootools.net) Copyright (C) Valerio Proietti 7 | * 8 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 9 | */ 10 | Element.include({ 11 | /** 12 | * Stops all the visual effects on the element 13 | * 14 | * @return Element this 15 | */ 16 | stop: function() { 17 | fx_cancel_all(this); 18 | return this; 19 | }, 20 | 21 | /** 22 | * hides the element with given visual effect 23 | * 24 | * @param String fx name 25 | * @param Object fx options 26 | * @return Element this 27 | */ 28 | hide: function(fx, options) { 29 | return (fx && this.visible()) ? call_fx(this, fx, ['out', options]) : this.$super(); 30 | }, 31 | 32 | /** 33 | * shows the element with the given visual effect 34 | * 35 | * @param String fx name 36 | * @param Object fx options 37 | * @return Element this 38 | */ 39 | show: function(fx, options) { 40 | return (fx && !this.visible()) ? call_fx(this, fx, ['in', options]) : this.$super(); 41 | }, 42 | 43 | /** 44 | * Toggles the element state with visual effect 45 | * 46 | * @param String fx name 47 | * @param Object fx options 48 | * @return Element this 49 | */ 50 | toggle: function(fx, options) { 51 | return fx ? call_fx(this, fx, ['toggle', options]) : this.$super(); 52 | }, 53 | 54 | /** 55 | * Removes the element out of the DOM structure 56 | * 57 | * @param String fx name 58 | * @param Object fx options 59 | * @return Element this 60 | */ 61 | remove: function(fx, options) { 62 | return (fx && this.visible()) ? call_fx(this, fx, ['out', $ext(options || {}, { 63 | onFinish: this.$super.bind(this) 64 | })]) : this.$super(); 65 | }, 66 | 67 | /** 68 | * runs the Fx.Morth effect to the given style 69 | * 70 | * @param style Object style 71 | * @param options Object optional effect options 72 | * @return Element self 73 | */ 74 | morph: function(style, options) { 75 | return call_fx(this, 'morph', [style, options || {}]); // <- don't replace with arguments 76 | }, 77 | 78 | /** 79 | * highlights the element 80 | * 81 | * @param start String start color 82 | * @param end String optional end color 83 | * @param Object effect options 84 | * @return Element self 85 | */ 86 | highlight: function() { 87 | return call_fx(this, 'highlight', arguments); 88 | }, 89 | 90 | /** 91 | * runs the Fx.Fade effect on the element 92 | * 93 | * @param mixed fade direction 'in' 'out' or a float number 94 | * @return Element self 95 | */ 96 | fade: function() { 97 | return call_fx(this, 'fade', arguments); 98 | }, 99 | 100 | /** 101 | * runs the Fx.Slide effect on the element 102 | * 103 | * @param String 'in' or 'out' 104 | * @param Object effect options 105 | * @return Element self 106 | */ 107 | slide: function() { 108 | return call_fx(this, 'slide', arguments); 109 | }, 110 | 111 | /** 112 | * Starts the smooth scrolling effect 113 | * 114 | * @param position Object {x: NNN, y: NNN} where to scroll 115 | * @param options Object fx-options 116 | * @return Element this 117 | */ 118 | scroll: function(value, options) { 119 | return call_fx(this, 'scroll', [value, options||{}]); 120 | }, 121 | 122 | /** 123 | * wraps the old scroll to be able to run it with fxes 124 | * 125 | * If you send two hashes then will start a smooth scrolling 126 | * otherwise will just jump over with the usual method 127 | * 128 | * @return Element this 129 | */ 130 | scrollTo: function(value, options) { 131 | return isHash(options) ? this.scroll(value, options) : this.$super.apply(this, arguments); 132 | } 133 | }); 134 | 135 | /** 136 | * Calls the visual effect on the element 137 | * 138 | * @param Element context 139 | * @param String fx-name 140 | * @param Object fx-options 141 | * @return Element context 142 | */ 143 | function call_fx(element, name, params) { 144 | var args = $A(params).compact(), 145 | options = isHash(args.last()) ? args.pop() : {}, 146 | fx = new Fx[name.capitalize()](element, options); 147 | 148 | fx.start.apply(fx, args); 149 | 150 | return element; 151 | } 152 | -------------------------------------------------------------------------------- /src/fx/fx/attr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * An abstract attributes based Fx 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | Fx.Attr = new Class(Fx, { 7 | 8 | prepare: function(attrs) { 9 | this.before = {}; 10 | this.after = attrs; 11 | var key, element = this.element._; 12 | 13 | for (key in attrs) { 14 | this.before[key] = element[key]; 15 | } 16 | }, 17 | 18 | render: function(delta) { 19 | var key, element = this.element._, before = this.before; 20 | for (key in before) { 21 | element[key] = before[key] + (this.after[key] - before[key]) * delta; 22 | } 23 | } 24 | 25 | }); -------------------------------------------------------------------------------- /src/fx/fx/fade.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The opacity effects wrapper 3 | * 4 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 5 | */ 6 | Fx.Fade = new Class(Fx.Twin, { 7 | prepare: function(how) { 8 | this.setHow(how); 9 | 10 | if (this.how === 'in') { 11 | // calling 'prototype' to prevent circular calls from subclasses 12 | Element.prototype.show.call(this.element.setStyle({opacity: 0})); 13 | } 14 | 15 | return this.$super({opacity: this.how === 'in' ? 1 : 0}); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /src/fx/fx/highlight.js: -------------------------------------------------------------------------------- 1 | /** 2 | * the elements hightlighting effect 3 | * 4 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 5 | */ 6 | Fx.Highlight = 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 = 'backgroundColor', 27 | end_color = end || element.getStyle(style_name); 28 | 29 | if (is_transparent(end_color)) { 30 | this.onFinish(function() { element_style[style_name] = 'transparent'; }); 31 | 32 | // trying to find the end color 33 | end_color = [element].concat(element.parents()) 34 | .map('getStyle', style_name) 35 | .reject(is_transparent) 36 | .compact().first() || '#FFF'; 37 | } 38 | 39 | element_style[style_name] = (start || this.options.color); 40 | 41 | return this.$super({backgroundColor: end_color}); 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /src/fx/fx/scroll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A smooth scrolling visual effect 3 | * 4 | * Copyright (C) 2009-2011 Nikolay Nemshilov 5 | */ 6 | Fx.Scroll = new Class(Fx.Attr, { 7 | 8 | initialize: function(element, options) { 9 | element = $(element); 10 | // swapping the actual scrollable when it's the window 11 | this.$super( 12 | element instanceof Window ? 13 | element._.document[ 14 | Browser.WebKit ? 'body' : 'documentElement' 15 | ] : element, 16 | options 17 | ); 18 | }, 19 | 20 | prepare: function(value) { 21 | var attrs = {}; 22 | 23 | if ('x' in value) { attrs.scrollLeft = value.x; } 24 | if ('y' in value) { attrs.scrollTop = value.y; } 25 | 26 | this.$super(attrs); 27 | } 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /src/fx/fx/slide.js: -------------------------------------------------------------------------------- 1 | /** 2 | * the slide effects wrapper 3 | * 4 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 5 | */ 6 | Fx.Slide = new Class(Fx.Twin, { 7 | extend: { 8 | Options: Object.merge(Fx.Options, { 9 | direction: 'top' 10 | }) 11 | }, 12 | 13 | // protected 14 | prepare: function(how) { 15 | this.setHow(how); 16 | 17 | // calling 'prototype' to prevent circular calls from subclasses 18 | var element = Element.prototype.show.call(this.element), 19 | element_style = element._.style, 20 | old_styles = Object.only( 21 | element_style, 22 | 'overflow', 'width', 'height', 23 | 'marginTop', 'marginLeft' 24 | ); 25 | 26 | function restore_styles() { 27 | for (var key in old_styles) { 28 | element_style[key] = old_styles[key]; 29 | } 30 | } 31 | 32 | this.onFinish(restore_styles).onCancel(restore_styles); 33 | 34 | element_style.overflow = 'hidden'; 35 | 36 | return this.$super(fx_slide_prepare_styles( 37 | element_style, 38 | element.size(), 39 | this.options.direction, 40 | this.how 41 | )); 42 | } 43 | }); 44 | 45 | function fx_slide_prepare_styles(element_style, size, direction, how) { 46 | var style = {}, 47 | margin_left = element_style.marginLeft.toFloat() || 0, 48 | margin_top = element_style.marginTop.toFloat() || 0, 49 | to_right = direction === 'right', 50 | to_bottom = direction === 'bottom', 51 | vertical = direction === 'top' || to_bottom; 52 | 53 | if (how === 'out') { 54 | style[vertical ? 'height' : 'width'] = '0px'; 55 | 56 | if (vertical) { 57 | element_style.height = size.y + 'px'; 58 | } else { 59 | element_style.width = size.y + 'px'; 60 | } 61 | 62 | if (to_right) { 63 | style.marginLeft = margin_left + size.x+'px'; 64 | } else if (to_bottom) { 65 | style.marginTop = margin_top + size.y +'px'; 66 | } 67 | } else { 68 | if (vertical) { 69 | style.height = size.y + 'px'; 70 | element_style.height = '0px'; 71 | } else { 72 | style.width = size.x + 'px'; 73 | element_style.width = '0px'; 74 | } 75 | 76 | if (to_right) { 77 | style.marginLeft = margin_left + 'px'; 78 | element_style.marginLeft = margin_left + size.x + 'px'; 79 | } else if (to_bottom) { 80 | style.marginTop = margin_top + 'px'; 81 | element_style.marginTop = margin_top + size.y + 'px'; 82 | } 83 | } 84 | 85 | return style; 86 | } 87 | -------------------------------------------------------------------------------- /src/fx/fx/twin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this is a superclass for the bidirectional effects 3 | * 4 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 5 | */ 6 | Fx.Twin = new Class(Fx.Morph, { 7 | 8 | /** 9 | * hides the element if it meant to be switched off 10 | * 11 | * @return Fx self 12 | */ 13 | finish: function() { 14 | if (this.how === 'out') { 15 | // calling 'prototype' to prevent circular calls from subclasses 16 | Element.prototype.hide.call(this.element); 17 | } 18 | 19 | return this.$super(); 20 | }, 21 | 22 | // protected 23 | 24 | /** 25 | * assigns the direction of the effect in or out 26 | * 27 | * @param String 'in', 'out' or 'toggle', 'toggle' by default 28 | */ 29 | setHow: function(how) { 30 | this.how = how || 'toggle'; 31 | 32 | if (this.how === 'toggle') { 33 | this.how = this.element.visible() ? 'out' : 'in'; 34 | } 35 | } 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /src/fx/string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * There are the String unit extensions for the effects library 3 | * 4 | * Copyright (C) 2008-2009 Nikolay V. Nemshilov 5 | */ 6 | String.COLORS = { 7 | maroon: '#800000', 8 | red: '#ff0000', 9 | orange: '#ffA500', 10 | yellow: '#ffff00', 11 | olive: '#808000', 12 | purple: '#800080', 13 | fuchsia: '#ff00ff', 14 | white: '#ffffff', 15 | lime: '#00ff00', 16 | green: '#008000', 17 | navy: '#000080', 18 | blue: '#0000ff', 19 | aqua: '#00ffff', 20 | teal: '#008080', 21 | black: '#000000', 22 | silver: '#c0c0c0', 23 | gray: '#808080', 24 | brown: '#a52a2a' 25 | }; 26 | 27 | String.include({ 28 | /** 29 | * converts a #XXX or rgb(X, X, X) sring into standard #XXXXXX color string 30 | * 31 | * @return String hex color 32 | */ 33 | toHex: function() { 34 | var match = /^#(\w)(\w)(\w)$/.exec(this); 35 | 36 | if (match) { 37 | match = "#"+ match[1]+match[1]+match[2]+match[2]+match[3]+match[3]; 38 | } else if ((match = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/.exec(this))) { 39 | match = "#"+ match.slice(1).map(function(bit) { 40 | bit = (bit-0).toString(16); 41 | return bit.length == 1 ? '0'+bit : bit; 42 | }).join(''); 43 | } else { 44 | match = String.COLORS[this] || this; 45 | } 46 | 47 | return match; 48 | }, 49 | 50 | /** 51 | * converts a hex string into an rgb array 52 | * 53 | * @param boolean flag if need an array 54 | * @return String rgb(R,G,B) or Array [R,G,B] 55 | */ 56 | toRgb: function(array) { 57 | var match = /#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})/i.exec(this.toHex()||''); 58 | 59 | if (match) { 60 | match = match.slice(1).map('toInt', 16); 61 | match = array ? match : 'rgb('+match+')'; 62 | } 63 | 64 | return match; 65 | } 66 | }); 67 | -------------------------------------------------------------------------------- /src/lang/function.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Function class extentions 3 | * 4 | * Credits: 5 | * Some of the functionality inspired by 6 | * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson 7 | * 8 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 9 | */ 10 | Function.include({ 11 | /** 12 | * binds the function to be executed in the given scope 13 | * 14 | * @param Object scope 15 | * @param mixed optional curry (left) argument 16 | * .... 17 | * @return Function binded function 18 | */ 19 | bind: function() { 20 | var args = $A(arguments), scope = args.shift(), func = this; 21 | return function() { 22 | return func.apply(scope, 23 | (args.length !== 0 || arguments.length !== 0) ? 24 | args.concat($A(arguments)) : args 25 | ); 26 | }; 27 | }, 28 | 29 | /** 30 | * binds the function as an event listener to the given scope object 31 | * 32 | * @param Object scope 33 | * @param mixed optional curry (left) argument 34 | * ....... 35 | * @return Function binded function 36 | */ 37 | bindAsEventListener: function() { 38 | var args = $A(arguments), scope = args.shift(), func = this; 39 | return function(event) { 40 | return func.apply(scope, [event].concat(args).concat($A(arguments))); 41 | }; 42 | }, 43 | 44 | /** 45 | * allows you to put some curry in your cookery 46 | * 47 | * @param mixed value to curry 48 | * .... 49 | * @return Function curried function 50 | */ 51 | curry: function() { 52 | return this.bind.apply(this, [this].concat($A(arguments))); 53 | }, 54 | 55 | /** 56 | * The right side curry feature 57 | * 58 | * @param mixed value to curry 59 | * .... 60 | * @return Function curried function 61 | */ 62 | rcurry: function() { 63 | var curry = $A(arguments), func = this; 64 | return function() { 65 | return func.apply(func, $A(arguments).concat(curry)); 66 | }; 67 | }, 68 | 69 | /** 70 | * delays the function execution 71 | * 72 | * @param Integer delay ms 73 | * @param mixed value to curry 74 | * ..... 75 | * @return Integer timeout marker 76 | */ 77 | delay: function() { 78 | var args = $A(arguments), timeout = args.shift(), 79 | timer = new Number(setTimeout(this.bind.apply(this, [this].concat(args)), timeout)); 80 | 81 | timer.cancel = function() { clearTimeout(this); }; 82 | 83 | return timer; 84 | }, 85 | 86 | /** 87 | * creates a periodical execution of the function with the given timeout 88 | * 89 | * @param Integer delay ms 90 | * @param mixed value to curry 91 | * ... 92 | * @return Ineger interval marker 93 | */ 94 | periodical: function() { 95 | var args = $A(arguments), timeout = args.shift(), 96 | timer = new Number(setInterval(this.bind.apply(this, [this].concat(args)), timeout)); 97 | 98 | timer.stop = function() { clearInterval(this); }; 99 | 100 | return timer; 101 | }, 102 | 103 | /** 104 | * Chains the given function after the current one 105 | * 106 | * @param Function the next function 107 | * @param mixed optional value to curry 108 | * ...... 109 | * @return Function chained function 110 | */ 111 | chain: function() { 112 | var args = $A(arguments), func = args.shift(), current = this; 113 | return function() { 114 | var result = current.apply(current, arguments); 115 | func.apply(func, args); 116 | return result; 117 | }; 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /src/lang/json.js: -------------------------------------------------------------------------------- 1 | if (!window.JSON) { 2 | window.JSON = (function() { 3 | var 4 | // see the original JSON decoder implementation for descriptions http://www.json.org/json2.js 5 | cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 6 | specials = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'}, 7 | quotables = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 8 | 9 | 10 | // quotes the string 11 | function quote(string) { 12 | return string.replace(quotables, function(chr) { 13 | return specials[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4); 14 | }); 15 | } 16 | 17 | // adds the leading zero symbol 18 | function zerofy(num) { 19 | return (num < 10 ? '0' : '')+num; 20 | } 21 | 22 | return { 23 | stringify: function(value) { 24 | switch(typeof(value)) { 25 | case 'boolean': return String(value); 26 | case 'number': return String(value+0); 27 | case 'string': return '"'+ quote(value) + '"'; 28 | case 'object': 29 | if (value === null) { 30 | return 'null'; 31 | } else if (isArray(value)) { 32 | return '['+$A(value).map(JSON.stringify).join(',')+']'; 33 | 34 | } else if (to_s.call(value) === '[object Date]') { 35 | return '"' + value.getUTCFullYear() + '-' + 36 | zerofy(value.getUTCMonth() + 1) + '-' + 37 | zerofy(value.getUTCDate()) + 'T' + 38 | zerofy(value.getUTCHours()) + ':' + 39 | zerofy(value.getUTCMinutes()) + ':' + 40 | zerofy(value.getUTCSeconds()) + '.' + 41 | zerofy(value.getMilliseconds()) + 'Z' + 42 | '"'; 43 | 44 | } else { 45 | var result = [], key; 46 | for (key in value) { 47 | result.push('"'+key+'":'+JSON.stringify(value[key])); 48 | } 49 | return '{'+result.join(',')+'}'; 50 | } 51 | } 52 | }, 53 | 54 | parse: function(string) { 55 | if (isString(string) && string) { 56 | // getting back the UTF-8 symbols 57 | string = string.replace(cx, function (a) { 58 | return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 59 | }); 60 | 61 | // checking the JSON string consistency 62 | if (/^[\],:{}\s]*$/.test(string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') 63 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') 64 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 65 | return new Function('return '+string)(); 66 | } 67 | } 68 | 69 | throw "JSON parse error: "+string; 70 | } 71 | }; 72 | })(); 73 | } -------------------------------------------------------------------------------- /src/lang/math.js: -------------------------------------------------------------------------------- 1 | /** 2 | * here are the starndard Math object extends 3 | * 4 | * Credits: 5 | * The idea of random mehtod is taken from 6 | * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto 7 | * 8 | * Copyright (C) 2008-2010 Nikolay Nemshilov 9 | */ 10 | var Math_old_random = Math.random; 11 | 12 | /** 13 | * the standard random method replacement, to make it more useful 14 | * 15 | * USE: 16 | * Math.random(); // original functionality, returns a float between 0 and 1 17 | * Math.random(10); // returns an integer between 0 and 10 18 | * Math.random(1,4); // returns an integer between 1 and 4 19 | * 20 | * @param min Integer minimum value if there's two arguments and maximum value if there's only one 21 | * @param max Integer maximum value 22 | * @return Float random between 0 and 1 if there's no arguments or an integer in the given range 23 | */ 24 | Math.random = function(min, max) { 25 | 26 | if (arguments.length === 0) { 27 | return Math_old_random(); 28 | } else if (arguments.length === 1) { 29 | max = min; 30 | min = 0; 31 | } 32 | 33 | return ~~(Math_old_random() * (max-min+1) + ~~min); 34 | }; 35 | -------------------------------------------------------------------------------- /src/lang/number.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Number class extentions 3 | * 4 | * Credits: 5 | * Some methods inspired by 6 | * - Ruby (http://www.ruby-lang.org) Copyright (C) Yukihiro Matsumoto 7 | * 8 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 9 | */ 10 | Number.include({ 11 | /** 12 | * executes the given callback the given number of times 13 | * 14 | * @param Function callback 15 | * @param Object optional callback execution scope 16 | * @return void 17 | */ 18 | times: function(callback, scope) { 19 | for (var i=0; i < this; i++) { 20 | callback.call(scope, i); 21 | } 22 | return this; 23 | }, 24 | 25 | upto: function(number, callback, scope) { 26 | for (var i=this+0; i <= number; i++) { 27 | callback.call(scope, i); 28 | } 29 | return this; 30 | }, 31 | 32 | downto: function(number, callback, scope) { 33 | for (var i=this+0; i >= number; i--) { 34 | callback.call(scope, i); 35 | } 36 | return this; 37 | }, 38 | 39 | /** 40 | * Maps a list of numbers from current to given 41 | * or map a result of calls of the callback on those numbers 42 | * 43 | * @param {Number} end number 44 | * @param {Function} optional callback 45 | * @param {Object} optional callback scope 46 | * @return {Array} the result list 47 | */ 48 | to: function(number, callback, scope) { 49 | var start = this + 0, end = number, result = [], i=start; 50 | 51 | callback = callback || function(i) { return i; }; 52 | 53 | if (end > start) { 54 | for (; i <= end; i++) { 55 | result.push(callback.call(scope, i)); 56 | } 57 | } else { 58 | for (; i >= end; i--) { 59 | result.push(callback.call(scope, i)); 60 | } 61 | } 62 | 63 | return result; 64 | }, 65 | 66 | abs: function() { 67 | return Math.abs(this); 68 | }, 69 | 70 | round: function(size) { 71 | return size ? parseFloat(this.toFixed(size)) : Math.round(this); 72 | }, 73 | 74 | ceil: function() { 75 | return Math.ceil(this); 76 | }, 77 | 78 | floor: function() { 79 | return Math.floor(this); 80 | }, 81 | 82 | min: function(value) { 83 | return this < value ? value : this + 0; 84 | }, 85 | 86 | max: function(value) { 87 | return this > value ? value : this + 0; 88 | } 89 | }); 90 | -------------------------------------------------------------------------------- /src/lang/object.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Object class extentions 3 | * 4 | * Credits: 5 | * Some functionality is inspired by 6 | * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson 7 | * 8 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 9 | */ 10 | $ext(Object, { 11 | /** 12 | * extracts the list of the attribute names of the given object 13 | * 14 | * @param Object object 15 | * @return Array keys list 16 | */ 17 | keys: function(object) { 18 | var keys = [], key; 19 | for (key in object) { 20 | keys.push(key); 21 | } 22 | return keys; 23 | }, 24 | 25 | /** 26 | * extracts the list of the attribute values of the given object 27 | * 28 | * @param Object object 29 | * @return Array values list 30 | */ 31 | values: function(object) { 32 | var values = [], key; 33 | for (key in object) { 34 | values.push(object[key]); 35 | } 36 | return values; 37 | }, 38 | 39 | /** 40 | * Calls the function with every key/value pair on the hash 41 | * 42 | * @param in Object the data hash 43 | * @param Function the callback 44 | * @param scope Object an optional scope 45 | * @return Object the original hash 46 | */ 47 | each: function(object, callback, scope) { 48 | for (var key in object) { 49 | callback.call(scope, key, object[key]); 50 | } 51 | 52 | return object; 53 | }, 54 | 55 | /** 56 | * checks if the object-hash has no keys 57 | * 58 | * @param Object object 59 | * @return check result 60 | */ 61 | empty: function(object) { 62 | for (var key in object) { return false; } 63 | return true; 64 | }, 65 | 66 | /** 67 | * A simple cloning method 68 | * NOTE: does not clone the things recoursively! 69 | * 70 | * @param Object object 71 | * @return Object clone 72 | */ 73 | clone: function(object) { 74 | return Object.merge(object); 75 | }, 76 | 77 | /** 78 | * returns a copy of the object which contains 79 | * all the same keys/values except the key-names 80 | * passed the the method arguments 81 | * 82 | * @param Object object 83 | * @param String key-name to exclude 84 | * ..... 85 | * @return Object filtered copy 86 | */ 87 | without: function() { 88 | var filter = $A(arguments), object = filter.shift(), copy = {}, key; 89 | 90 | for (key in object) { 91 | if (!filter.include(key)) { 92 | copy[key] = object[key]; 93 | } 94 | } 95 | 96 | return copy; 97 | }, 98 | 99 | /** 100 | * returns a copy of the object which contains all the 101 | * key/value pairs from the specified key-names list 102 | * 103 | * NOTE: if some key does not exists in the original object, it will be just skipped 104 | * 105 | * @param Object object 106 | * @param String key name to exclude 107 | * ..... 108 | * @return Object filtered copy 109 | */ 110 | only: function() { 111 | var filter = $A(arguments), object = filter.shift(), copy = {}, 112 | i=0, length = filter.length; 113 | 114 | for (; i < length; i++) { 115 | if (filter[i] in object) { 116 | copy[filter[i]] = object[filter[i]]; 117 | } 118 | } 119 | 120 | return copy; 121 | }, 122 | 123 | /** 124 | * merges the given objects and returns the result 125 | * 126 | * NOTE this method _DO_NOT_ change the objects, it creates a new object 127 | * which conatins all the given ones. 128 | * if there is some keys introspections, the last object wins. 129 | * all non-object arguments will be omitted 130 | * 131 | * @param first Object object 132 | * @param second Object mixing 133 | * ...... 134 | * @return Object merged object 135 | */ 136 | merge: function() { 137 | var object = {}, i=0, args=arguments, l=args.length, key; 138 | for (; i < l; i++) { 139 | if (isHash(args[i])) { 140 | for (key in args[i]) { 141 | object[key] = isHash(args[i][key]) && !(args[i][key] instanceof Class) ? 142 | Object.merge(key in object ? object[key] : {}, args[i][key]) : args[i][key]; 143 | } 144 | } 145 | } 146 | return object; 147 | }, 148 | 149 | /** 150 | * converts a hash-object into an equivalent url query string 151 | * 152 | * @param Object object 153 | * @return String query 154 | */ 155 | toQueryString: function(object) { 156 | var entries = to_query_string_map(object), i=0, result = []; 157 | 158 | for (; i < entries.length; i++) { 159 | result.push(encodeURIComponent(entries[i][0]) + "=" + encodeURIComponent(''+entries[i][1])); 160 | } 161 | 162 | return result.join('&'); 163 | } 164 | }, true); 165 | 166 | // private 167 | 168 | /** 169 | * pre-converts nested objects into a flat key-value structure 170 | * 171 | * @param {Object} data-hash 172 | * @param {String} key-prefix 173 | * @return {Array} key-value pairs 174 | */ 175 | function to_query_string_map(hash, prefix) { 176 | var result = [], key, value, i; 177 | 178 | for (key in hash) { 179 | value = hash[key]; 180 | if (prefix) { 181 | key = prefix + "["+ key + "]"; 182 | } 183 | 184 | if (typeof(value) === 'object') { 185 | if (isArray(value)) { 186 | if (!key.endsWith('[]')) { 187 | key += "[]"; 188 | } 189 | for (i=0; i < value.length; i++) { 190 | result.push([key, value[i]]); 191 | } 192 | } else if (value) { // assuming it's an object 193 | value = to_query_string_map(value, key); 194 | for (i=0; i < value.length; i++) { 195 | result.push(value[i]); 196 | } 197 | } 198 | } else { 199 | result.push([key, value]); 200 | } 201 | } 202 | 203 | return result; 204 | } -------------------------------------------------------------------------------- /src/lang/regexp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Regexp class extentions 3 | * 4 | * Credits: 5 | * Inspired by 6 | * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson 7 | * 8 | * Copyright (C) 2008-2010 Nikolay V. Nemshilov 9 | */ 10 | 11 | 12 | /** 13 | * Escapes the string for safely use as a regular expression 14 | * 15 | * @param String raw string 16 | * @return String escaped string 17 | */ 18 | RegExp.escape = function(string) { 19 | return (''+string).replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1'); 20 | }; 21 | -------------------------------------------------------------------------------- /src/lang/string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The String class extentions 3 | * 4 | * Credits: 5 | * Some of the functionality inspired by 6 | * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson 7 | * The trim function taken from work of Steven Levithan 8 | * - http://blog.stevenlevithan.com/archives/faster-trim-javascript 9 | * 10 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 11 | */ 12 | String.include({ 13 | /** 14 | * checks if the string is an empty string 15 | * 16 | * @return boolean check result 17 | */ 18 | empty: function() { 19 | return this == ''; 20 | }, 21 | 22 | /** 23 | * checks if the string contains only white-spaces 24 | * 25 | * @return boolean check result 26 | */ 27 | blank: function() { 28 | return this == false; 29 | }, 30 | 31 | /** 32 | * removes trailing whitespaces 33 | * 34 | * @return String trimmed version 35 | */ 36 | trim: String.prototype.trim || function() { 37 | var str = this.replace(/^\s\s*/, ''), i = str.length; 38 | while ((/\s/).test(str.charAt(--i))) {} 39 | return str.slice(0, i + 1); 40 | }, 41 | 42 | /** 43 | * returns a copy of the string with all the tags removed 44 | * @return String without tags 45 | */ 46 | stripTags: function() { 47 | return this.replace(/<\/?[^>]+>/ig, ''); 48 | }, 49 | 50 | /** 51 | * removes all the scripts declarations out of the string 52 | * @param mixed option. If it equals true the scrips will be executed, 53 | * if a function the scripts will be passed in it 54 | * @return String without scripts 55 | */ 56 | stripScripts: function(option) { 57 | var scripts = '', text = this.replace( 58 | /]*>([\s\S]*?)<\/script>/img, 59 | function(match, source) { 60 | scripts += source + "\n"; 61 | return ''; 62 | } 63 | ); 64 | 65 | if (option === true) { 66 | $eval(scripts); 67 | } else if (isFunction(option)) { 68 | option(scripts, text); 69 | } 70 | 71 | return text; 72 | }, 73 | 74 | /** 75 | * extracts all the scripts out of the string 76 | * 77 | * @return String the extracted stcripts 78 | */ 79 | extractScripts: function() { 80 | var scripts = ''; 81 | this.stripScripts(function(s) { scripts = s; }); 82 | return scripts; 83 | }, 84 | 85 | /** 86 | * evals all the scripts in the string 87 | * 88 | * @return String self (unchanged version with scripts still in their place) 89 | */ 90 | evalScripts: function() { 91 | this.stripScripts(true); 92 | return this; 93 | }, 94 | 95 | /** 96 | * converts underscored or dasherized string to a camelized one 97 | * @returns String camelized version 98 | */ 99 | camelize: function() { 100 | return this.replace(/(\-|_)+(.)?/g, function(match, dash, chr) { 101 | return chr ? chr.toUpperCase() : ''; 102 | }); 103 | }, 104 | 105 | /** 106 | * converts a camelized or dasherized string into an underscored one 107 | * @return String underscored version 108 | */ 109 | underscored: function() { 110 | return this.replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-/g, '_').toLowerCase(); 111 | }, 112 | 113 | /** 114 | * returns a capitalised version of the string 115 | * 116 | * @return String captialised version 117 | */ 118 | capitalize: function() { 119 | return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); 120 | }, 121 | 122 | /** 123 | * Makes a dashed version of the string 124 | * 125 | * @return String dashed version 126 | */ 127 | dasherize: function() { 128 | return this.underscored().replace(/_/g, '-'); 129 | }, 130 | 131 | /** 132 | * checks if the string contains the given substring 133 | * 134 | * @param String string 135 | * @return boolean check result 136 | */ 137 | includes: function(string) { 138 | return this.indexOf(string) != -1; 139 | }, 140 | 141 | /** 142 | * checks if the string starts with the given substring 143 | * 144 | * @param String string 145 | * @param boolean ignore the letters case 146 | * @return boolean check result 147 | */ 148 | startsWith: function(string, ignorecase) { 149 | return (ignorecase !== true ? this.indexOf(string) : 150 | this.toLowerCase().indexOf(string.toLowerCase()) 151 | ) === 0; 152 | }, 153 | 154 | /** 155 | * checks if the string ends with the given substring 156 | * 157 | * @param String substring 158 | * @param boolean ignore the letters case 159 | * @return boolean check result 160 | */ 161 | endsWith: function(string, ignorecase) { 162 | var index = ignorecase !== true ? 163 | this.lastIndexOf(string) : 164 | this.toLowerCase().lastIndexOf(string.toLowerCase()); 165 | return index > -1 && this.length - index === string.length; 166 | }, 167 | 168 | /** 169 | * converts the string to an integer value 170 | * @param Integer base 171 | * @return Integer or NaN 172 | */ 173 | toInt: function(base) { 174 | return parseInt(this, base === undefined ? 10 : base); 175 | }, 176 | 177 | /** 178 | * converts the string to a float value 179 | * @param boolean flat if the method should not use a flexible matching 180 | * @return Float or NaN 181 | */ 182 | toFloat: function(strict) { 183 | return parseFloat(strict === true ? this : 184 | this.replace(',', '.').replace(/(\d)-(\d)/, '$1.$2')); 185 | } 186 | 187 | }); 188 | 189 | String.prototype.include = String.prototype.includes; 190 | -------------------------------------------------------------------------------- /src/olds/events.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Making the 'submit' and 'change' events bubble under IE browsers 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | 7 | /** 8 | * Tests if there is the event support 9 | * 10 | * @param String event name 11 | * @retrun Boolean check result 12 | */ 13 | function event_support_for(name, tag) { 14 | var e = document.createElement(tag); 15 | e.setAttribute(name, ';'); 16 | return isFunction(e[name]); 17 | } 18 | 19 | if (!event_support_for('onsubmit', 'form')) { 20 | /** 21 | * Emulates the 'submit' event bubbling for IE browsers 22 | * 23 | * @param raw dom-event 24 | * @return void 25 | */ 26 | var submit_boobler = function(raw_event) { 27 | var event = $(raw_event), element = event.target._, 28 | type = element.type, form = element.form, parent; 29 | 30 | if (form && (parent = $(form).parent()) && ( 31 | (raw_event.keyCode === 13 && (type === 'text' || type === 'password')) || 32 | (raw_event.type === 'click' && (type === 'submit' || type === 'image')) 33 | )) { 34 | event.type = 'submit'; 35 | event.target = $(form); 36 | parent.fire(event); 37 | } 38 | }; 39 | 40 | document.attachEvent('onclick', submit_boobler); 41 | document.attachEvent('onkeypress', submit_boobler); 42 | } 43 | 44 | if (!event_support_for('onchange', 'input')) { 45 | 46 | var get_input_value = function(target) { 47 | var element = target._, 48 | type = element.type; 49 | 50 | return type === 'radio' || type === 'checkbox' ? 51 | element.checked : target.getValue(); 52 | }, 53 | 54 | /** 55 | * Emulates the 'change' event bubbling 56 | * 57 | * @param Event wrapped dom-event 58 | * @param Input wrapped input element 59 | * @return void 60 | */ 61 | change_boobler = function(event, target) { 62 | var parent = target.parent(), 63 | value = get_input_value(target); 64 | 65 | if (parent && ''+target._prev_value !== ''+value) { 66 | target._prev_value = value; // saving the value so it didn't fire up again 67 | event.type = 'change'; 68 | parent.fire(event); 69 | } 70 | }, 71 | 72 | /** 73 | * Catches the input field changes 74 | * 75 | * @param raw dom-event 76 | * @return void 77 | */ 78 | catch_inputs_access = function(raw_event) { 79 | var event = $(raw_event), 80 | target = event.target, 81 | type = target._.type, 82 | tag = target._.tagName, 83 | input_is_radio = (type === 'radio' || type === 'checkbox'); 84 | 85 | if ( 86 | (event.type === 'click' && (input_is_radio || tag === 'SELECT')) || 87 | (event.type === 'keydown' && ( 88 | (event.keyCode == 13 && (tag !== 'TEXTAREA')) || 89 | type === 'select-multiple' 90 | )) 91 | ) { 92 | change_boobler(event, target); 93 | } 94 | }, 95 | 96 | /** 97 | * Catch inputs blur 98 | * 99 | * @param raw dom-event 100 | * @return void 101 | */ 102 | catch_input_left = function(raw_event) { 103 | var event = $(raw_event), 104 | target = event.target; 105 | 106 | if (target instanceof Input) { 107 | change_boobler(event, target); 108 | } 109 | }; 110 | 111 | document.attachEvent('onclick', catch_inputs_access); 112 | document.attachEvent('onkeydown', catch_inputs_access); 113 | document.attachEvent('onfocusout', catch_input_left); 114 | 115 | /** 116 | * storing the input element previous value, so we could figure out 117 | * if it was changed later on 118 | */ 119 | document.attachEvent('onbeforeactivate', function(event) { 120 | var element = $(event).target; 121 | 122 | if (element instanceof Input) { 123 | element._prev_value = get_input_value(element); 124 | } 125 | }); 126 | } 127 | -------------------------------------------------------------------------------- /src/olds/ie.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Old IE browser hacks 3 | * 4 | * Keep them in one place so they were more compact 5 | * 6 | * Copyright (C) 2009-2011 Nikolay Nemshilov 7 | */ 8 | if (RightJS.Browser.OLD && RightJS.Browser.IE) { 9 | // loads DOM element extensions for selected elements 10 | window.$ = RightJS.$ = (function(old_function) { 11 | return function(id) { 12 | var element = old_function(id); 13 | 14 | // old IE browses match both, ID and NAME 15 | if (element && element instanceof RightJS.Element && 16 | RightJS.isString(id) && element._.id !== id 17 | ) { 18 | element = RightJS.$(document).first('#'+ id); 19 | } 20 | 21 | return element; 22 | }; 23 | })(RightJS.$); 24 | } 25 | -------------------------------------------------------------------------------- /src/olds/konq.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Konqueror browser fixes 3 | * 4 | * Copyright (C) 2009-2011 Nikolay V. Nemshilov 5 | */ 6 | 7 | /** 8 | * manual position calculator, it works for Konqueror and also 9 | * for old versions of Opera and FF 10 | */ 11 | if (!RightJS.$E('p')._.getBoundingClientRect) { 12 | RightJS.Element.include({ 13 | position: function() { 14 | var element = this._, 15 | top = element.offsetTop, 16 | left = element.offsetLeft, 17 | parent = element.offsetParent; 18 | 19 | while (parent) { 20 | top += parent.offsetTop; 21 | left += parent.offsetLeft; 22 | 23 | parent = parent.offsetParent; 24 | } 25 | 26 | return {x: left, y: top}; 27 | } 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /src/olds/loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The old browsers support patch loading script 3 | * will be included in the core file when it's built 4 | * with the no-olds option 5 | * 6 | * Basically it just checks all the script tags on the page 7 | * finds the core inclusion tag and uses it's src attribute 8 | * to dynamically load the olds patch 9 | * 10 | * Copyright (C) 2009-2011 Nikolay V. Nemshilov 11 | */ 12 | if (RightJS.Browser.OLD) { 13 | (function(d) { 14 | var script = d.createElement('script'), 15 | scripts = d.getElementsByTagName('script'), 16 | rjs_spt = scripts[scripts.length - 1]; 17 | 18 | script.src = rjs_spt.src.replace(/(^|\/)(right)([^\/]+)$/, '$1$2-olds$3'); 19 | 20 | rjs_spt.parentNode.appendChild(script); 21 | })(document); 22 | } 23 | -------------------------------------------------------------------------------- /src/right.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The framework description object 3 | * 4 | * Copyright (C) 2008-2011 Nikolay Nemshilov 5 | */ 6 | var RightJS = function(value) { 7 | return value; // <- a dummy method to emulate the safe-mode 8 | }; 9 | 10 | RightJS.version = "%{version}"; 11 | RightJS.modules =["%{modules}"]; 12 | 13 | -------------------------------------------------------------------------------- /src/xhr/element.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this module contains the Element unit XHR related extensions 3 | * 4 | * Credits: 5 | * - jQuery (http://jquery.com) Copyright (C) John Resig 6 | * 7 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 8 | */ 9 | Element.include({ 10 | /** 11 | * performs an Xhr request to the given url 12 | * and updates the element internals with the responseText 13 | * 14 | * @param String url address 15 | * @param Object xhr options 16 | * @return Element this 17 | */ 18 | load: function(url, options) { 19 | new Xhr(url, $ext({method: 'get'}, options)).update(this); 20 | return this; 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /src/xhr/form.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Here are the Form unit Xhr extensions 3 | * 4 | * Credits: 5 | * Some of the functionality inspired by 6 | * - Prototype (http://prototypejs.org) Copyright (C) Sam Stephenson 7 | * - jQuery (http://jquery.com) Copyright (C) John Resig 8 | * 9 | * Copyright (C) 2009-2011 Nikolay V. Nemshilov 10 | */ 11 | Form.include({ 12 | /** 13 | * sends the form via xhr request 14 | * 15 | * @param Options xhr request options 16 | * @return Form this 17 | */ 18 | send: function(options) { 19 | options = options || {}; 20 | options.method = options.method || this._.method || 'post'; 21 | 22 | this.xhr = new Xhr( 23 | this._.action || document.location.href, 24 | $ext({spinner: this.first('.spinner')}, options) 25 | ) 26 | .onComplete(this.enable.bind(this)) 27 | .onCancel(this.enable.bind(this)) 28 | .send(this); 29 | 30 | this.disable.bind(this).delay(1); // webkit needs this async call with iframed calls 31 | return this; 32 | }, 33 | 34 | /** 35 | * Cancels current Xhr request (if there are any) 36 | * 37 | * @return Form this 38 | */ 39 | cancelXhr: function() { 40 | if (this.xhr instanceof Xhr) { 41 | this.xhr.cancel(); 42 | } 43 | 44 | return this; 45 | }, 46 | 47 | /** 48 | * makes the form be remote by default 49 | * 50 | * @param Object default options 51 | * @return Form this 52 | */ 53 | remotize: function(options) { 54 | if (!this.remote) { 55 | this.on('submit', Form_remote_send, options); 56 | this.remote = true; 57 | } 58 | 59 | return this; 60 | }, 61 | 62 | /** 63 | * removes the remote call hook 64 | * 65 | * @return Form this 66 | */ 67 | unremotize: function() { 68 | this.stopObserving('submit', Form_remote_send); 69 | this.remote = false; 70 | return this; 71 | } 72 | }); 73 | 74 | /** 75 | * Catches the form submit events and sends the form remotely 76 | * 77 | * @param Event submit 78 | * @param Object xhr options 79 | * @return void 80 | */ 81 | function Form_remote_send(event, options) { 82 | event.stop(); 83 | this.send(options); 84 | } 85 | 86 | /** 87 | * Adds Xhr params handling if a Form element is passed to Xhr#send 88 | * 89 | * @param Object params - could be Hash or Form element 90 | * @return Object 91 | */ 92 | Xhr.include({ 93 | prepareParams: function(params) { 94 | if (params && params instanceof Form) { 95 | this.form = params; 96 | params = params.values(); 97 | } 98 | return params; 99 | } 100 | }); 101 | -------------------------------------------------------------------------------- /src/xhr/xhr/dummy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A dummy XmlHTTPRequest interface to be used in other 3 | * fake requests 4 | * 5 | * Copyright (C) 2010-2011 Nikolay Nemshilov 6 | */ 7 | Xhr.Dummy = { 8 | open: function() {}, 9 | setRequestHeader: function() {}, 10 | onreadystatechange: function() {} 11 | }; 12 | -------------------------------------------------------------------------------- /src/xhr/xhr/iframed.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This unit presents a fake drop in replacement for the XmlHTTPRequest unit 3 | * but works with an iframe targeting in the background 4 | * 5 | * Copyright (C) 2008-2011 Nikolay Nemshilov 6 | */ 7 | Xhr.IFramed = new Class({ 8 | include: Xhr.Dummy, 9 | 10 | /** 11 | * constructor 12 | * 13 | * @param Form form which will be submitted via the frame 14 | * @return void 15 | */ 16 | initialize: function(form) { 17 | this.form = form; 18 | this.id = 'xhr_'+ new Date().getTime(); 19 | 20 | this.form.doc().first('body').append('', 22 | 'after'); 23 | 24 | $(this.id).on('load', this.onLoad.bind(this)); 25 | }, 26 | 27 | send: function() { 28 | this.form.set('target', this.id).submit(); 29 | }, 30 | 31 | onLoad: function() { 32 | this.status = 200; 33 | this.readyState = 4; 34 | 35 | this.form.set('target', ''); 36 | 37 | try { 38 | this.responseText = window[this.id].document.documentElement.innerHTML; 39 | } catch(e) { } 40 | 41 | this.onreadystatechange(); 42 | }, 43 | 44 | abort: function() { 45 | $(this.id).set('src', 'about:blank'); 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /src/xhr/xhr/jsonp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The JSONP Xhr request tonnel 3 | * 4 | * Copyright (C) 2010-2011 Nikolay Nemshilov 5 | */ 6 | Xhr.JSONP = new Class({ 7 | include: Xhr.Dummy, 8 | 9 | prefix: 'jsonp', 10 | 11 | /** 12 | * Constructor 13 | * 14 | * @param Xhr the actual xhr request object 15 | * @return void 16 | */ 17 | initialize: function(xhr) { 18 | this.xhr = xhr; 19 | this.name = this.prefix + new Date().getTime(); 20 | this.param = (isString(xhr.jsonp) ? 21 | xhr.jsonp : 'callback') + "=" + this.name; 22 | 23 | this.script = $E('script', { 24 | charset: xhr.encoding, 25 | async: xhr.async 26 | }); 27 | }, 28 | 29 | /** 30 | * saving the url and method for the further use 31 | * 32 | * @param method String request method 33 | * @param address String request url address 34 | * @param Boolean async request marker 35 | * @return void 36 | */ 37 | open: function(method, url, async) { 38 | this.url = url; 39 | this.method = method; 40 | }, 41 | 42 | /** 43 | * Sends the actual request by inserting the script into the document body 44 | * 45 | * @param String data 46 | * @return void 47 | */ 48 | send: function(data) { 49 | window[this.name] = this.finish.bind(this); 50 | 51 | this.script.set('src', this.url + (this.url.include('?') ? '&' : '?') + this.param + "&" + data) 52 | .insertTo($$('script').last(), 'after'); 53 | }, 54 | 55 | /** 56 | * Receives the actual JSON data from the server 57 | * 58 | * @param Object JSON data 59 | * @return void 60 | */ 61 | finish: function(data) { 62 | this.status = 200; 63 | this.readyState = 4; 64 | 65 | this.xhr.json = this.xhr.responseJSON = data; 66 | 67 | this.onreadystatechange(); 68 | }, 69 | 70 | /** 71 | * We can't really cancel a JSONP request 72 | * but we can prevent the default handler to ckick in 73 | * 74 | * @return void 75 | */ 76 | abort: function() { 77 | window[this.name] = function() {}; 78 | } 79 | }); 80 | -------------------------------------------------------------------------------- /test/effects.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS - Effects Test 5 | 6 | 7 | 8 | 26 | 59 | 60 | 61 |

Test Morph

62 |

63 |

64 |
Morph
65 |
66 | 72 | Run 73 | Reset 74 |

75 | 76 |

Test Highlight

77 |

78 |

79 |
Hightlight
80 |
81 | 82 | Run 83 | Reset 84 |

85 | 86 |

Test Fade

87 |

88 |

89 |
Fade
90 |
91 | 92 | Run 93 | Reset 94 |

95 | 96 |

Test Slide

97 |

98 |

99 |
Slide
100 |
101 | 102 | Run 103 | Reset 104 |

105 | 106 |

Test Scroll

107 |

108 |

109 |
110 |
111 | 115 | Run 116 | Reset 117 | 122 |

123 | 124 |

Test Background Position

125 |

126 |

127 |
BG Position
128 |
129 | 136 | Run 137 | Reset 138 |

139 | 140 |

Test Stopping Fx

141 |

142 |

143 |
Stop Me
144 |
145 | 153 | Run 154 | Reset 155 |

156 | 157 |
158 | 159 | 160 | -------------------------------------------------------------------------------- /test/form/form-cancel-xhr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS Form - Cancel Xhr Test 5 | 6 | 9 | 46 | 51 | 52 | 53 | 54 |

Simple Xhr Cancel Test

55 |
56 | 57 | 58 | Spinner 59 |
60 | 61 |

IFramed Request Test

62 |
63 | 64 | 65 | 66 | Spinner 67 |
68 | 69 |

JSONP Request Test

70 |
71 | 72 | 73 | Spinner 74 |
75 | 76 | -------------------------------------------------------------------------------- /test/form/server.rb: -------------------------------------------------------------------------------- 1 | # 2 | # A simple server to emulate slow requests to the server 3 | # so that we could test the Form#cancelXhr method 4 | # 5 | # Copyright (C) 2011 Nikolay Nemshilov 6 | # 7 | 8 | require 'rubygems' 9 | require 'sinatra' 10 | 11 | get '/' do 12 | sleep 3 13 | if params[:callback] 14 | "#{params[:callback]}(#{params[:response]})" 15 | else 16 | params[:response] || '' 17 | end 18 | end 19 | 20 | post '/' do 21 | sleep 3 22 | params[:response] || '' 23 | end -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS - Test 5 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/mouseio.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS - Mouse IO Test 5 | 6 | 7 | 43 | 97 | 98 | 99 | 100 |

MouseEnter/MouseLeave Events Test

101 |

102 |

103 |

Direct Handling

104 |
105 |
106 |
Boo!
107 |
108 |
109 |
110 | 111 |
112 |

UJS Handling

113 |
114 |
115 |
Boo!
116 |
117 |
118 |
119 | 120 |
121 |

122 | 123 |

More Complicated Case

124 |

125 |

126 |
    127 |
  • Some sort of text
  • 128 |
  • Some sort of text
  • 129 |
  • Some sort of text
  • 130 |
  • Some sort of text
  • 131 |
  • Some sort of text
  • 132 |
  • Some sort of text
  • 133 |
134 |
135 |

136 | 137 |

138 | 139 | -------------------------------------------------------------------------------- /test/safe/core_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The core units test 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | var CoreTest = TestCase.create({ 7 | name: 'CoreTest', 8 | 9 | testRightJS: function() { 10 | this.assert('RightJS' in window); 11 | this.assert(typeof(RightJS) == 'function'); 12 | }, 13 | 14 | testClass: function() { 15 | this.assert('Class' in RightJS); 16 | this.assertFalse('Class' in window); 17 | 18 | var Klass = new RightJS.Class({ 19 | initialize: function() { 20 | this.initialized = true; 21 | }, 22 | 23 | callMommy: function() { 24 | this.mommyCalled = true; 25 | } 26 | }); 27 | 28 | var k = new Klass(); 29 | k.callMommy(); 30 | 31 | this.assert(k.initialized); 32 | this.assert(k.mommyCalled); 33 | }, 34 | 35 | testObserver: function() { 36 | this.assert('Observer' in RightJS); 37 | this.assertFalse('Observer' in window); 38 | 39 | var result; 40 | 41 | new RightJS.Observer() 42 | .on('boo', function() { result = 'hoo'; }) 43 | .fire('boo'); 44 | 45 | this.assertEqual('hoo', result); 46 | }, 47 | 48 | testOptions: function() { 49 | this.assert('Options' in RightJS); 50 | this.assertFalse('Options' in window); 51 | 52 | with (RightJS) { 53 | var Klass = new Class({ 54 | include: Options, 55 | 56 | extend: { 57 | Options: { 58 | boo: 'boo' 59 | } 60 | }, 61 | 62 | initialize: function(options) { 63 | this.setOptions(options); 64 | } 65 | }); 66 | } 67 | 68 | var k = new Klass({ 69 | hoo: 'hoo' 70 | }); 71 | 72 | this.assert(k.options); 73 | this.assertEqual('boo', k.options.boo); 74 | this.assertEqual('hoo', k.options.hoo); 75 | }, 76 | 77 | test$ext: function() { 78 | this.assert('$ext' in RightJS); 79 | this.assertFalse('$ext' in window); 80 | 81 | this.assertEqual({ 82 | boo: 'boo', 83 | hoo: 'hoo' 84 | }, RightJS.$ext({boo: 'boo'}, {hoo: 'hoo'})); 85 | }, 86 | 87 | test$eval: function() { 88 | this.assert('$eval' in RightJS); 89 | this.assertFalse('$eval' in window); 90 | 91 | window.____boo = null; 92 | RightJS.$eval('window.____boo = "hoo";'); 93 | 94 | this.assertEqual("hoo", window.____boo, "it should eval the script in the context of the current window"); 95 | } 96 | }); 97 | -------------------------------------------------------------------------------- /test/safe/dom_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The dom features test 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | var doc_ready = false, win_ready = false; 7 | RightJS.$(document).onReady(function() { doc_ready = true; }); 8 | 9 | var DomTest = TestCase.create({ 10 | name: 'DomTest', 11 | 12 | beforeAll: function() { 13 | this.el = document.createElement('div'); 14 | this.el.id = 'test-div'; 15 | this.el.innerHTML = '' + 16 | '
'+ 17 | '
two
'+ 18 | '
three
'+ 19 | '
four
'+ 20 | '
'; 21 | 22 | document.body.appendChild(this.el); 23 | }, 24 | 25 | afterAll: function() { 26 | document.body.removeChild(this.el); 27 | }, 28 | 29 | testExtendingAnElement: function() { 30 | var el = document.createElement('div'); 31 | 32 | this.assertFalse('set' in el); 33 | 34 | this.assert(RightJS.$(el) instanceof RightJS.Element); 35 | 36 | this.assert('set' in RightJS.$(el)); 37 | }, 38 | 39 | testFindById: function() { 40 | var el = RightJS.$('test-div'); 41 | 42 | this.assert(el instanceof RightJS.Element); 43 | this.assertSame(this.el, el._, "checking if we found the same element"); 44 | }, 45 | 46 | testFindByCss: function() { 47 | var els = RightJS.$$('#test-div div.one div'); 48 | 49 | this.assert(els instanceof RightJS.Array); 50 | this.assertEqual(3, els.length); 51 | this.assertEqual(['two', 'three', 'four'], [].concat(els.map('_').map('innerHTML'))); 52 | }, 53 | 54 | testEvents: function() { 55 | var el = RightJS.$(this.el); 56 | 57 | this.assert('onClick' in el); 58 | 59 | var ev = null; 60 | el.onClick(function(e) { ev = e; }); 61 | 62 | this.fireClick(this.el); 63 | 64 | this.assertNotNull(ev); 65 | this.assert('stop' in ev); 66 | }, 67 | 68 | testDocumentAccess: function() { 69 | var doc = RightJS.$(document); 70 | 71 | this.assert(doc instanceof RightJS.Document); 72 | }, 73 | 74 | testWindowAccess: function() { 75 | var win = RightJS.$(window); 76 | 77 | this.assert(win instanceof RightJS.Window); 78 | }, 79 | 80 | testDocumentReady: function() { 81 | this.assert(doc_ready); 82 | } 83 | }); 84 | -------------------------------------------------------------------------------- /test/safe/lang_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The native units access tests 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | var LangTest = TestCase.create({ 7 | name: 'LangTest', 8 | 9 | testNumber: function() { 10 | this.assert('times' in RightJS.Number.prototype); 11 | this.assertFalse('times' in Number.prototype); 12 | 13 | var num = new RightJS.Number(10); 14 | var nums = []; 15 | 16 | if (!('times' in num)) return; // due a Safari issue 17 | 18 | this.assert('times' in num); 19 | 20 | num.times(function(i) { nums.push(i); }); 21 | 22 | this.assertEqual([0,1,2,3,4,5,6,7,8,9], nums); 23 | }, 24 | 25 | testNumberQuickAccess: function() { 26 | var num = RightJS(10); 27 | var nums = []; 28 | 29 | this.assert(num instanceof RightJS.Number); 30 | 31 | num.times(function(i) { nums.push(i); }); 32 | 33 | this.assertEqual([0,1,2,3,4,5,6,7,8,9], nums); 34 | }, 35 | 36 | testString: function() { 37 | this.assert('endsWith' in RightJS.String.prototype); 38 | this.assertFalse('endsWith' in String.prototype); 39 | 40 | var str = new RightJS.String('boo.hoo'); 41 | 42 | if (!('endsWith' in str)) return; // due a Safari issue 43 | 44 | this.assert('endsWith' in str); 45 | this.assert(str.endsWith('.hoo')); 46 | 47 | this.assertEqual('boo.hoo', ''+ str); 48 | }, 49 | 50 | testStringQuickAccess: function() { 51 | var str = RightJS('boo.hoo'); 52 | 53 | this.assert(str instanceof RightJS.String); 54 | this.assert(str.endsWith('.hoo')); 55 | 56 | this.assertEqual('boo.hoo', ''+str); 57 | }, 58 | 59 | testFunction: function() { 60 | this.assert('curry' in RightJS.Function.prototype); 61 | this.assertFalse('curry' in Function.prototype); 62 | 63 | var func = new RightJS.Function('a,b', 'return [a,b];'); 64 | 65 | if (!('curry' in func)) return; // due a Safari issue 66 | 67 | this.assert(func instanceof RightJS.Function); 68 | 69 | var result = func.curry('A')('B'); 70 | 71 | this.assertEqual('A', result[0]); 72 | this.assertEqual('B', result[1]); 73 | }, 74 | 75 | testFunctionQuickAccess: function() { 76 | var A, B; 77 | var func1 = function(a, b) { A = a, B = b }; 78 | 79 | this.assertFalse('curry' in func1); 80 | 81 | var func2 = RightJS(func1); 82 | 83 | this.assertSame(func1, func2); 84 | this.assert('curry' in func1); 85 | 86 | func2.curry('A')('B'); 87 | 88 | this.assertEqual('A', A); 89 | this.assertEqual('B', B); 90 | }, 91 | 92 | testArray: function() { 93 | this.assert('without' in RightJS.Array.prototype); 94 | this.assertFalse('without' in Array.prototype); 95 | 96 | var arr1 = new RightJS.Array('a', 'b', 'c'); 97 | 98 | if (!('without' in arr1)) return; // due a Safari issue 99 | 100 | this.assert('without' in arr1); 101 | 102 | var arr2 = arr1.without('b'); 103 | this.assert(arr2 instanceof RightJS.Array); 104 | this.assertEqual(['a', 'c'], [].concat(arr2)); 105 | }, 106 | 107 | testArrayQuickAccess: function() { 108 | var arr1 = RightJS.$A([1,2,3]); 109 | 110 | this.assert(arr1 instanceof RightJS.Array); 111 | this.assertEqual([1,2,3], [].concat(arr1)); 112 | 113 | var arr2 = RightJS([1,2,3]); 114 | this.assert(arr2 instanceof RightJS.Array); 115 | this.assertEqual([1,2,3], [].concat(arr2)); 116 | } 117 | }); 118 | -------------------------------------------------------------------------------- /test/tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this script builds and runs all the tests 3 | */ 4 | run_tests({ 5 | UtilTest: 'unit/core/util', 6 | 7 | ObjectTest: 'unit/lang/object', 8 | MathTest: 'unit/lang/math', 9 | ArrayTest: 'unit/lang/array', 10 | StringTest: 'unit/lang/string', 11 | FunctionTest: 'unit/lang/function', 12 | NumberTest: 'unit/lang/number', 13 | RegexpTest: 'unit/lang/regexp', 14 | JsonTest: 'unit/lang/json', 15 | 16 | ClassTest: 'unit/core/class', 17 | OptionsTest: 'unit/core/options', 18 | ObserverTest: 'unit/core/observer', 19 | 20 | BrowserTest: 'unit/dom/browser', 21 | CookieTest: 'unit/dom/cookie', 22 | 23 | WrapperTest: 'unit/dom/wrapper', 24 | 25 | EventTest: 'unit/dom/event', 26 | EventBubblingTest: 'unit/dom/event/bubbling', 27 | EventDelegationTest: 'unit/dom/event/delegation', 28 | 29 | ElementTest: 'unit/dom/element', 30 | ElementStylesTest: 'unit/dom/element/styles', 31 | ElementCommonsTest: 'unit/dom/element/commons', 32 | ElementStructsTest: 'unit/dom/element/structs', 33 | ElementDimensionsTest: 'unit/dom/element/dimensions', 34 | ElementEventsTest: 'unit/dom/element/events', 35 | 36 | SelectorTest: 'unit/dom/selector', 37 | 38 | FormTest: 'unit/dom/form', 39 | InputTest: 'unit/dom/input', 40 | 41 | DomReadyTest: 'unit/dom/ready', 42 | WindowTest: 'unit/dom/window', 43 | 44 | XhrTest: 'unit/xhr/xhr', 45 | XhrIFramedTest: 'unit/xhr/xhr/iframed', 46 | XhrJSONPTest: 'unit/xhr/xhr/jsonp', 47 | 48 | FxTest: 'unit/fx/fx', 49 | FxMorphTest: 'unit/fx/fx/morph' 50 | }); 51 | -------------------------------------------------------------------------------- /test/tests.safe.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The safe-mode tests aggregator 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | run_tests({ 7 | CoreTest: 'safe/core', 8 | LangTest: 'safe/lang', 9 | DomTest: 'safe/dom' 10 | }); 11 | -------------------------------------------------------------------------------- /test/tests.server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The server-side version tests (run on node.js) 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | var sys = require('sys'); 7 | var rjs = require('../build/right-server'); 8 | 9 | /** 10 | * Creates a string of given size 11 | * filled in with given symbols 12 | * 13 | * @param String initial 14 | * @param Integer length 15 | * @param String fill in sybmol 16 | * @return String padded one 17 | */ 18 | function ljust(text, length, symbol) { 19 | length = length || 80; 20 | symbol = symbol || ' '; 21 | 22 | for (var str='', i=0; i < length; i++) { 23 | str += i < text.length ? text[i] : symbol; 24 | } 25 | 26 | return str; 27 | } 28 | 29 | var total = 0; 30 | var failed = 0; 31 | 32 | /** 33 | * A simple test reporter 34 | * 35 | * @param Boolean test result 36 | * @param String message 37 | * @return void 38 | */ 39 | function assert(result, message) { 40 | total ++; 41 | result || failed ++; 42 | 43 | result = result ? ' \u001B[32mOK\u001B[0m ' : ' \u001B[31mFAIL\u001B[0m '; 44 | 45 | console.log(ljust(" * "+message, 74) + result); 46 | } 47 | 48 | /** 49 | * asserts equality of things 50 | * 51 | * @param mixed expected 52 | * @param mixed actual 53 | * @param String message 54 | * @return void 55 | */ 56 | function assert_equal(expected, received, message) { 57 | assert(sys.inspect(expected) == sys.inspect(received), message); 58 | } 59 | 60 | // the header 61 | console.log("\u001B[36m" + ljust("== RightJS server-side version build test =====", 80, '=') + "\u001B[0m") 62 | 63 | 64 | /** 65 | * The actual tests go here 66 | */ 67 | assert(rjs.RightJS !== null, "RightJS presense"); 68 | 69 | // checking the utility methods 70 | assert_equal({a:1, b:2}, rjs.$ext({a:1}, {b:2}), "$ext()"); 71 | assert(!rjs.defined(undefined) && rjs.defined(1), "defined()"); 72 | assert(!rjs.isFunction(1) && rjs.isFunction(assert), "isFunction()"); 73 | assert(!rjs.isString(1) && rjs.isString('1'), "isString()"); 74 | assert(!rjs.isNumber('1') && rjs.isNumber(1), "isNumber()"); 75 | assert(!rjs.isHash(1) && rjs.isHash({}), "isHash()"); 76 | assert(!rjs.isArray(1) && rjs.isArray([]), "isArray()"); 77 | 78 | // checking the language extensions 79 | assert_equal([1,2].first(), 1, "Array#first()"); 80 | assert_equal([1,2].last(), 2, "Array#last()"); 81 | assert_equal([1,2].size(), 2, "Array#size()"); 82 | assert_equal([1.1, 2.2].map('floor'), [1,2], "Array#map()") 83 | assert_equal(['boo', 'hoo'].filter('include', 'bo'), ['boo'], "Array#filter()"); 84 | assert_equal(['boo', 'hoo'].reject('include', 'bo'), ['hoo'], "Array#reject()"); 85 | 86 | 87 | // testing the Class 88 | var A = new rjs.Class(); 89 | assert(typeof(A) === 'function', 'Class creation'); 90 | var a = new A(); 91 | assert(a instanceof A, 'Class instantiation'); 92 | var B = new rjs.Class(A, {}); 93 | var b = new B(); 94 | assert(b instanceof B && b instanceof A, 'Class inheritance'); 95 | 96 | 97 | // the footer 98 | console.log(ljust('', 80, '_')) 99 | console.log("Total: "+total+" Tests, "+ failed + " Failed"); -------------------------------------------------------------------------------- /test/ujs-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RightJS UJS Test 5 | 6 | 33 | 83 | 84 | 85 | 86 |

Simple Clicks Catcher

87 |

88 |

89 |

Bottom one is wired

90 |
91 |
92 |
Boo!
93 |
94 |
95 |
96 | 97 |
98 |

99 | 100 |

Hovers Handler

101 |

102 |

103 |

Bottom one is wired

104 |
105 |
106 |
Boo!
107 |
108 |
109 |
110 | 111 |
112 |

113 | 114 |

Focus/Blur Test

115 |

116 |

117 |

Bottom one is wired

118 |
119 |
120 | 121 |
122 |
123 |
124 | 125 |
126 |

127 | 128 |

129 | 130 | -------------------------------------------------------------------------------- /test/unit/core/options_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Options module unit-tests 3 | * 4 | * Copyright (C) 2008-2010 Nikolay V. Nemshilov 5 | */ 6 | 7 | var OptionsTest = TestCase.create({ 8 | name: 'OptionsTest', 9 | 10 | 11 | testInstanceLevel: function() { 12 | var Klass = new Class({ 13 | include: Options, 14 | 15 | Options: { 16 | a: 1, 17 | b: 2 18 | }, 19 | 20 | initialize: function(options) { 21 | this.setOptions(options); 22 | } 23 | }); 24 | 25 | this.assertEqual(Klass.prototype.Options, new Klass().options); 26 | this.assertEqual({a: 1, b: 4}, new Klass({b:4}).options); 27 | }, 28 | 29 | testClassLevel: function() { 30 | var Klass = new Class({ 31 | include: Options, 32 | 33 | extend: { 34 | Options: { 35 | a: 1, 36 | b: 2 37 | } 38 | }, 39 | 40 | initialize: function(options) { 41 | this.setOptions(options); 42 | } 43 | }); 44 | 45 | this.assertEqual(Klass.Options, new Klass().options); 46 | this.assertEqual({a: 1, b: 4}, new Klass({b:4}).options); 47 | }, 48 | 49 | testClassLevelWithInheritance: function() { 50 | var Klass1 = new Class({ 51 | include: Options, 52 | 53 | extend: { 54 | Options: { 55 | a: 1, 56 | b: 2 57 | } 58 | }, 59 | 60 | initialize: function(options) { 61 | this.setOptions(options); 62 | } 63 | }); 64 | 65 | var Klass2 = new Class(Klass1, {}); 66 | var Klass3 = new Class(Klass2, {}); 67 | var Klass = new Class(Klass3, {}); 68 | 69 | this.assertEqual(Klass1.Options, new Klass().options); 70 | this.assertEqual({a: 1, b: 4}, new Klass({b:4}).options); 71 | }, 72 | 73 | testWithObserver: function() { 74 | var Klass = new Class(Observer, { 75 | include: Options, 76 | 77 | initialize: function(options) { 78 | this.$super(); 79 | this.setOptions(options); 80 | } 81 | }); 82 | 83 | var the_function = function() {}; 84 | var klass = new Klass({ 85 | onFinish: the_function 86 | }); 87 | 88 | this.assert(klass.observes('finish', the_function)); 89 | this.assertFalse('onFinish' in klass.options) 90 | }, 91 | 92 | testCutOptions: function() { 93 | var args = null; 94 | 95 | var Klass = new Class({ 96 | include: Options, 97 | 98 | initialize: function() { 99 | args = this.cutOptions(arguments); 100 | } 101 | }); 102 | 103 | var k = new Klass(1,2,3); 104 | this.assertEqual([1,2,3], args); 105 | this.assertEqual({}, k.options); 106 | 107 | var k = new Klass(1, {a:1}); 108 | this.assertEqual([1], args); 109 | this.assertEqual({a:1}, k.options); 110 | 111 | var k = new Klass(1, 2, {b:2}); 112 | this.assertEqual([1,2], args); 113 | this.assertEqual({b:2}, k.options); 114 | 115 | var k = new Klass({c:3}); 116 | this.assertEqual([], args); 117 | this.assertEqual({c:3}, k.options); 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /test/unit/dom/browser_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Browser unit test-case 3 | * 4 | * Copyright (C) 2008 Nikolay V. Nemshilov aka St. 5 | */ 6 | var BrowserTest = TestCase.create({ 7 | name: "BrowserTest", 8 | 9 | testIE: function() { 10 | this.assertEqual(this.util.Browser.IE, Browser.IE); 11 | }, 12 | 13 | testOpera: function() { 14 | this.assertEqual(this.util.Browser.Opera, Browser.Opera); 15 | }, 16 | 17 | testWebKit: function() { 18 | this.assertEqual(this.util.Browser.WebKit, Browser.WebKit); 19 | }, 20 | 21 | testGecko: function() { 22 | this.assertEqual(this.util.Browser.Gecko, Browser.Gecko); 23 | }, 24 | 25 | testMobileSafari: function() { 26 | this.assertEqual(this.util.Browser.MobileSafari, Browser.MobileSafari); 27 | }, 28 | 29 | testKonqueror: function() { 30 | this.assertEqual(this.util.Browser.Konqueror, Browser.Konqueror); 31 | } 32 | }); 33 | -------------------------------------------------------------------------------- /test/unit/dom/cookie_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Cookie class test 3 | * 4 | * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. 5 | */ 6 | var CookieTest = TestCase.create({ 7 | name: 'CookieTest' 8 | }); 9 | 10 | // local cookies sometimes don't work on IE, and probably might be switched off in any browser 11 | if (TestCaseUtil.Cookie.enabled()) { 12 | CookieTest.extend({ 13 | setUp: function() { 14 | this.tearDown(); 15 | }, 16 | 17 | tearDown: function() { 18 | var date = new Date(); 19 | date.setTime(date.getTime() - 24 * 60 * 60 * 1000); 20 | 21 | document.cookie = 'rjs_test=; expires='+date.toGMTString(); 22 | }, 23 | 24 | testInstance: function() { 25 | var cook = new Cookie('boo', {duration: 5}); 26 | 27 | this.assertEqual('boo', cook.name); 28 | this.assertEqual(false, cook.options.secure); 29 | this.assertSame(document, cook.options.document); 30 | this.assertEqual(5, cook.options.duration); 31 | }, 32 | 33 | testInstanceSet: function() { 34 | var cook = new Cookie('rjs_test'); 35 | 36 | this.assertSame(cook, cook.set('value')); 37 | this.assert(document.cookie.indexOf('rjs_test=value') != -1) 38 | }, 39 | 40 | testInstanceSetEscape: function() { 41 | new Cookie('rjs_test').set('asdf?%# ведмед'); 42 | 43 | this.assert(document.cookie.indexOf('rjs_test=asdf%3F%25%23%20%D0%B2%D0%B5%D0%B4%D0%BC%D0%B5%D0%B4') != -1); 44 | }, 45 | 46 | testInstanceGet: function() { 47 | var cook = new Cookie('rjs_test'); 48 | 49 | this.assertNull(cook.get()); 50 | 51 | document.cookie = 'rjs_test=test_value'; 52 | 53 | this.assertEqual('test_value', cook.get()); 54 | }, 55 | 56 | testInstanceGetUnescape: function() { 57 | document.cookie = 'rjs_test=asdf%3F%25%23%20%D0%B2%D0%B5%D0%B4%D0%BC%D0%B5%D0%B4'; 58 | 59 | this.assertEqual('asdf?%# ведмед', new Cookie('rjs_test').get()); 60 | }, 61 | 62 | testInstanceRemove: function() { 63 | document.cookie = 'rjs_test=test_value'; 64 | 65 | var cook = new Cookie('rjs_test'); 66 | 67 | this.assertSame(cook, cook.remove()); 68 | this.assert(document.cookie.indexOf('rjs_test=test_value') == -1); 69 | }, 70 | 71 | testClassSet: function() { 72 | var cook = Cookie.set('rjs_test', 'test_value', {duration: 5}); 73 | 74 | this.assertInstanceOf(Cookie, cook); 75 | this.assertEqual('rjs_test', cook.name); 76 | this.assertEqual(5, cook.options.duration); 77 | 78 | this.assert(document.cookie.indexOf('rjs_test=test_value') != -1); 79 | }, 80 | 81 | testClassGet: function() { 82 | this.assertNull(Cookie.get('rjs_test')); 83 | 84 | document.cookie = 'rjs_test=test_value'; 85 | 86 | this.assertEqual('test_value', Cookie.get('rjs_test')); 87 | }, 88 | 89 | testClassRemove: function() { 90 | document.cookie = 'rjs_test=test_value'; 91 | 92 | var cook = Cookie.remove('rjs_test'); 93 | 94 | this.assertInstanceOf(Cookie, cook); 95 | this.assertEqual('rjs_test', cook.name); 96 | 97 | this.assert(document.cookie.indexOf('rjs_test=test_value') == -1); 98 | }, 99 | 100 | testEnabled: function() { 101 | this.assert(Cookie.enabled()); 102 | }, 103 | 104 | testSavingObjects: function() { 105 | Cookie.set('test', {something: [1,2,3]}); 106 | this.assertEqual({something: [1,2,3]}, Cookie.get('test')); 107 | } 108 | }); 109 | } 110 | -------------------------------------------------------------------------------- /test/unit/dom/element/styles_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Element class styles related module functionality test 3 | * 4 | * Copyright (C) 2008-2010 Nikolay Nemshilov 5 | */ 6 | document.write('div.test---1234{font-size: 14px; display:none;}'); 7 | var ElementStylesTest = TestCase.create({ 8 | name: 'ElementStylesTest', 9 | 10 | setUp: function() { 11 | this.el = new Element('div'); 12 | }, 13 | 14 | testSetStyleWithKeyValue: function() { 15 | this.assertSame(this.el, this.el.setStyle('fontSize', '14px')); 16 | this.assertStyle(this.el._, {fontSize: '14px'}); 17 | }, 18 | 19 | testSetStyleAsHash: function() { 20 | var style = { 21 | fontSize: '14px', 22 | borderSize: '2px', 23 | display: 'block' 24 | } 25 | 26 | this.assertSame(this.el, this.el.setStyle(style)); 27 | this.assertStyle(this.el._, style); 28 | }, 29 | 30 | testSetStyleWithZero: function() { 31 | this.el._.style.zIndex = 10; 32 | this.el.setStyle('z-index', 0); 33 | 34 | this.assert(this.el._.style.zIndex == '0'); 35 | }, 36 | 37 | testGetStyleWithElementLevelStyles: function() { 38 | this.el.setStyle({ 39 | 'fontSize': '12px', 40 | 'borderSize': '2px' 41 | }); 42 | 43 | this.assertEqual('12px', this.el.getStyle('font-size')); 44 | this.assertEqual('2px', this.el.getStyle('borderSize')); 45 | }, 46 | 47 | testGetStyleWithCSSLevelStyles: function() { 48 | this.el._.className = 'test---1234'; 49 | document.body.appendChild(this.el._); 50 | 51 | this.assertEqual('14px', this.el.getStyle('font-size')); 52 | this.assertEqual('none', this.el.getStyle('display')); 53 | }, 54 | 55 | testSetOpacityStyle: function() { 56 | this.el.setStyle('opacity', 0.4); 57 | 58 | if (Browser.IE8L) { 59 | this.assertEqual('alpha(opacity=40)', this.el._.style.filter); 60 | } else { 61 | this.assertEqual('0.4', this.el._.style.opacity); 62 | } 63 | }, 64 | 65 | testGetOpacityStyle: function() { 66 | this.el.setStyle('opacity', 0.4); 67 | 68 | this.assertEqual('0.4', this.el.getStyle('opacity')); 69 | }, 70 | 71 | testSetFloatStyle: function() { 72 | this.el.setStyle('float', 'right'); 73 | 74 | this.assertEqual('right', this.el._.style[Browser.IE ? 'styleFloat' : 'cssFloat']); 75 | }, 76 | 77 | testGetFloatStyle: function() { 78 | this.el.setStyle('float', 'right'); 79 | 80 | this.assertEqual('right', this.el.getStyle('float')); 81 | }, 82 | 83 | testAssignStyleAsString: function() { 84 | this.el.setStyle('display: block; font-size: 12px'); 85 | this.assertEqual('block', this.el._.style.display); 86 | this.assertEqual('12px', this.el._.style.fontSize); 87 | }, 88 | 89 | testHasClass: function() { 90 | this.assert(!this.el.hasClass('foo')); 91 | this.assert(!this.el.hasClass('boo')); 92 | 93 | this.el._.className = 'foo'; 94 | 95 | this.assert( this.el.hasClass('foo')); 96 | this.assert(!this.el.hasClass('boo')); 97 | 98 | this.el._.className = 'foo boo'; 99 | 100 | this.assert(this.el.hasClass('foo')); 101 | this.assert(this.el.hasClass('boo')); 102 | }, 103 | 104 | testSetClass: function() { 105 | this.assertSame(this.el, this.el.setClass('foo bar')); 106 | this.assertEqual('foo bar', this.el._.className); 107 | }, 108 | 109 | testGetClass: function() { 110 | this.el._.className = 'boo hoo'; 111 | this.assertEqual('boo hoo', this.el.getClass()); 112 | }, 113 | 114 | testAddClass: function() { 115 | this.assertHasNoClassName(this.el._, 'foo'); 116 | this.assertHasNoClassName(this.el._, 'boo'); 117 | 118 | this.el.addClass('foo'); 119 | 120 | this.assertHasClassName(this.el._, 'foo'); 121 | this.assertHasNoClassName(this.el._, 'boo'); 122 | 123 | this.assertSame(this.el, this.el.addClass('boo'), "check if the method returns the element again"); 124 | 125 | this.assertHasClassName(this.el._, 'foo'); 126 | this.assertHasClassName(this.el._, 'boo'); 127 | 128 | this.el.addClass('boo'); 129 | this.assertEqual('foo boo', this.el._.className, "check if the class was not added twice"); 130 | }, 131 | 132 | testRemoveClass: function() { 133 | this.el._.className = 'foo boo'; 134 | 135 | this.el.removeClass('foo'); 136 | this.assertEqual('boo', this.el._.className); 137 | 138 | this.assertSame(this.el, this.el.removeClass('boo'), "check if the method returns the element again"); 139 | this.assertEqual('', this.el._.className); 140 | }, 141 | 142 | testToggleClass: function() { 143 | this.el.toggleClass('foo'); 144 | this.assertHasClassName(this.el._, 'foo'); 145 | 146 | this.assertSame(this.el, this.el.toggleClass('foo')); 147 | this.assertHasNoClassName(this.el._, 'foo'); 148 | }, 149 | 150 | testRadioClass: function() { 151 | var block = document.createElement('div'); 152 | var el1 = document.createElement('div'); 153 | var el2 = document.createElement('div'); 154 | var el3 = document.createElement('div'); 155 | 156 | block.appendChild(el1); 157 | block.appendChild(el2); 158 | block.appendChild(el3); 159 | 160 | $(el1).radioClass('test'); 161 | this.assertHasClassName(el1, 'test'); 162 | this.assertHasNoClassName(el2, 'test'); 163 | this.assertHasNoClassName(el3, 'test'); 164 | 165 | $(el2).radioClass('test'); 166 | this.assertHasNoClassName(el1, 'test'); 167 | this.assertHasClassName(el2, 'test'); 168 | this.assertHasNoClassName(el3, 'test'); 169 | 170 | $(el3).radioClass('test'); 171 | this.assertHasNoClassName(el1, 'test'); 172 | this.assertHasNoClassName(el2, 'test'); 173 | this.assertHasClassName(el3, 'test'); 174 | } 175 | }); 176 | -------------------------------------------------------------------------------- /test/unit/dom/element_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Element class unit-test 3 | * 4 | * Copyright (C) 2008-2010 Nikolay Nemshilov 5 | */ 6 | var ElementTest = TestCase.create({ 7 | name: 'ElementTest', 8 | 9 | testInstance: function() { 10 | this.assertEqual('DIV', new Element('div')._.tagName); 11 | this.assertEqual('TABLE', new Element('table')._.tagName); 12 | 13 | this.assert(new Element('div') instanceof RightJS.Element); 14 | this.assert(new Element('div') instanceof RightJS.Wrapper); 15 | }, 16 | 17 | testInstanceWithClass: function() { 18 | this.assertEqual('foo bla', new Element('div', { 19 | 'class': 'foo bla' 20 | })._.className); 21 | 22 | this.assertEqual('foo bla', new Element('div', { 23 | 'class': 'foo bla' 24 | })._.className); 25 | }, 26 | 27 | testInstanceWithStyle: function() { 28 | var style = { 29 | fontSize: '12px', 30 | borderSize: '12px', 31 | display: 'none' 32 | } 33 | this.assertStyle(new Element('div', { 34 | style: style 35 | })._, style); 36 | }, 37 | 38 | testInstanceWithAttributes: function() { 39 | var el = new Element('div', { 40 | id: 'el-id', 41 | title: 'el-title' 42 | })._; 43 | 44 | this.assertEqual('el-id', el.id); 45 | this.assertEqual('el-title', el.title); 46 | }, 47 | 48 | testInstanceWithEvents: function() { 49 | var el = new Element('div', { 50 | on: { 51 | click: function() {} 52 | } 53 | }); 54 | 55 | this.assert(el.observes('click')); 56 | }, 57 | 58 | testInstanceWithHtml: function() { 59 | var el = new Element('div', { 60 | html: "inner html" 61 | })._; 62 | 63 | this.assertEqual('inner html', el.innerHTML); 64 | }, 65 | 66 | testCheckboxConstruction: function() { 67 | var box1 = new Element('input', { 68 | type: 'checkbox', 69 | name: 'box1', 70 | checked: true, 71 | value: 'on' 72 | })._; 73 | 74 | this.assertEqual('checkbox', box1.type); 75 | this.assertEqual('box1', box1.name); 76 | this.assertEqual(true, box1.checked); 77 | 78 | var box2 = new Element('input', { 79 | type: 'radio', 80 | name: 'box2', 81 | checked: true, 82 | value: 'on' 83 | })._; 84 | 85 | this.assertEqual('radio', box2.type); 86 | this.assertEqual('box2', box2.name); 87 | this.assertEqual(true, box2.checked); 88 | 89 | if (Browser.OLD && !document.querySelector) { 90 | this.assertEqual( 91 | ''+ 92 | '', 93 | $E('div').insert([box1, box2])._.innerHTML.replace(/\s+_rjs_id="\d+"/mg, '') 94 | ); 95 | } 96 | }, 97 | 98 | testInclude: function() { 99 | var foo = function(title) { 100 | this.title = title; 101 | this.id = this.___bar(); 102 | return this; 103 | }; 104 | var bar = function() { return this.title + '-id' }; 105 | 106 | this.assertSame(Element, Element.include({ 107 | ___foo: foo, 108 | ___bar: bar 109 | })); 110 | 111 | this.assertSame(Element.prototype.___foo, foo); 112 | this.assertSame(Element.prototype.___bar, bar); 113 | 114 | // checking that the extensions a re working 115 | var div = new Element('div'); 116 | 117 | this.assertSame(div, div.___foo('some-title')); 118 | 119 | this.assertEqual('some-title', div.title); 120 | this.assertEqual('some-title-id', div.id); 121 | 122 | // checking the subclasses extensions 123 | var input = new Input(); 124 | 125 | this.assert(input.___foo instanceof Function); 126 | this.assert(input.___bar instanceof Function); 127 | 128 | this.assertSame(input, input.___foo('some-title')); 129 | this.assertEqual('some-title', input.title); 130 | this.assertEqual('some-title-id', input.id); 131 | }, 132 | 133 | testUIDMarkers: function() { 134 | var div = new Element('div', {html: 'test'}); 135 | var tmp = new Element('div').append(div); 136 | 137 | this.assertEqual('
test
', tmp.html().toLowerCase(), 138 | "checking that there are no UID markers in the HTML code"); 139 | } 140 | }); 141 | -------------------------------------------------------------------------------- /test/unit/dom/event/bubbling_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The artificaial events bubbling feature test 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | var EventBubblingTest = TestCase.create({ 7 | name: 'EventBubblingTest', 8 | 9 | testBubbling: function() { 10 | var e1 = new Element('div', {id: 'div-1'}); 11 | var e2 = new Element('div', {id: 'div-2'}); 12 | var e3 = new Element('div', {id: 'div-3'}); 13 | 14 | e1.insert(e2.insert(e3)); 15 | 16 | var event_1 = null; 17 | var event_2 = null; 18 | var event_3 = null; 19 | var target_1 = null; 20 | var target_2 = null; 21 | var target_3 = null; 22 | var current_1 = null; 23 | var current_2 = null; 24 | var current_3 = null; 25 | 26 | e1.on('click', function(event) { event_1 = event; target_1 = event.target; current_1 = event.currentTarget; }); 27 | e2.on('click', function(event) { event_2 = event; target_2 = event.target; current_2 = event.currentTarget; }); 28 | e3.on('click', function(event) { event_3 = event; target_3 = event.target; current_3 = event.currentTarget; }); 29 | 30 | e3.fire('click'); 31 | 32 | this.assertNotNull(event_3, "checking that the event was fired"); 33 | this.assertInstanceOf(RightJS.Event, event_3, "checking that the event was wrapped"); 34 | 35 | this.assert( 36 | event_1 === event_2 && event_2 === event_3, 37 | "checking that it was the same event object all the time" 38 | ); 39 | 40 | // checking we had correct target element in all three cases 41 | this.assertSame(e3, target_1); 42 | this.assertSame(e3, target_2); 43 | this.assertSame(e3, target_3); 44 | 45 | // checking that the 'currentTarget' was properly swapped during the bubble 46 | this.assertSame(e1, current_1); 47 | this.assertSame(e2, current_2); 48 | this.assertSame(e3, current_3); 49 | }, 50 | 51 | testStopBubbling: function() { 52 | var e1 = new Element('div', {id: 'div-1'}); 53 | var e2 = new Element('div', {id: 'div-2'}); 54 | var e3 = new Element('div', {id: 'div-3'}); 55 | 56 | e1.insert(e2.insert(e3)); 57 | 58 | var event_1 = null; 59 | var event_2 = null; 60 | var event_3 = null; 61 | var event_4 = null; 62 | 63 | e1.on('click', function(event) { event_1 = event; }); 64 | e2.on('click', function(event) { event_2 = event; event.stop(); }); 65 | e2.on('click', function(event) { event_3 = event; }); 66 | e3.on('click', function(event) { event_4 = event; }); 67 | 68 | e3.fire('click'); 69 | 70 | this.assertNull(event_1); 71 | 72 | this.assertNotNull(event_2); 73 | this.assertNotNull(event_3); 74 | this.assertNotNull(event_4); 75 | }, 76 | 77 | testBubblingOff: function() { 78 | var e1 = new Element('div', {id: 'div-1'}); 79 | var e2 = new Element('div', {id: 'div-2'}); 80 | var e3 = new Element('div', {id: 'div-3'}); 81 | 82 | e1.insert(e2.insert(e3)); 83 | 84 | var event_1 = null; 85 | var event_2 = null; 86 | var event_3 = null; 87 | 88 | e1.on('click', function(event) { event_1 = event; }); 89 | e2.on('click', function(event) { event_2 = event; }); 90 | e3.on('click', function(event) { event_3 = event; }); 91 | 92 | e3.fire('click', {bubbles: false}); 93 | 94 | this.assertNull(event_1); 95 | this.assertNull(event_2); 96 | 97 | this.assertNotNull(event_3); 98 | } 99 | }); 100 | -------------------------------------------------------------------------------- /test/unit/dom/event_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * the Event unit tests 3 | * 4 | * Copyright (C) 2008-2010 Nikolay V. Nemshilov 5 | */ 6 | var EventTest = TestCase.create({ 7 | name: 'EventTest', 8 | 9 | testDefaultExtending: function() { 10 | var mock = { 11 | type: 'event', 12 | which: 44, 13 | keyCode: 22, 14 | target: {a: 'target'}, 15 | currentTarget: {a: 'currentTarget'}, 16 | relatedTarget: {a: 'relatedTarget'}, 17 | pageX: 11, 18 | pageY: 33 19 | }; 20 | 21 | var event = new Event(mock); 22 | 23 | this.assert(event instanceof RightJS.Event); 24 | this.assert(event instanceof RightJS.Wrapper); 25 | 26 | this.assertSame(mock, event._); 27 | 28 | this.assertEqual(44, event.which); 29 | this.assertEqual(22, event.keyCode); 30 | this.assertEqual(11, event.pageX); 31 | this.assertEqual(33, event.pageY); 32 | 33 | this.assertSame(mock.target, event.target); 34 | this.assertSame(mock.currentTarget, event.currentTarget); 35 | this.assertSame(mock.relatedTarget, event.relatedTarget); 36 | }, 37 | 38 | testCustomEvent: function() { 39 | var event = new Event('custom', { 40 | foo: 'foo', 41 | bar: 'bar' 42 | }); 43 | 44 | this.assertInstanceOf(RightJS.Event, event); 45 | this.assertEqual('custom', event.type); 46 | this.assertEqual('foo', event._.foo); 47 | this.assertEqual('bar', event._.bar); 48 | this.assertEqual('foo', event.foo); 49 | this.assertEqual('bar', event.bar); 50 | }, 51 | 52 | testEventOffset: function() { 53 | var target = new Element('div'); 54 | var event = new Event('boo', { 55 | target: target 56 | }); 57 | 58 | target.position = function() { 59 | return { x: 100, y: 200 }; 60 | }; 61 | 62 | event.pageX = 110; 63 | event.pageY = 220; 64 | 65 | this.assertEqual({x: 10, y: 20}, event.offset()); 66 | }, 67 | 68 | // XXX: figure out how to get this run 69 | testEventOffsetTargetNotElement: function() { 70 | var target = null; 71 | var event = new Event('boo', { 72 | target: target 73 | }); 74 | 75 | event.pageX = 110; 76 | event.pageY = 220; 77 | 78 | this.assertEqual(null, event.offset()); 79 | } 80 | }); 81 | -------------------------------------------------------------------------------- /test/unit/dom/ready_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * the dom-ready module unit tests 3 | * 4 | * Copyright (C) 2009-2011 Nikolay Nemshilov 5 | */ 6 | var doc_ready_called = false; 7 | 8 | $(document).onReady(function() { doc_ready_called = true}); 9 | 10 | 11 | var DomReadyTest = TestCase.create({ 12 | name: 'DomReadyTest', 13 | 14 | testObserverExtensions: function() { 15 | this.assertNotNull('onReady' in $(document)); 16 | }, 17 | 18 | testLoadEventHandling: function() { 19 | var called; 20 | 21 | $(document).onReady(function() { called = true;}); 22 | 23 | $(document).fire('ready'); 24 | 25 | this.assert(called); 26 | }, 27 | 28 | testLoadCaptured: function() { 29 | this.assert(doc_ready_called); 30 | }, 31 | 32 | testReadyAlias: function() { 33 | this.assertNotNull('onReady' in $(document)); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /test/unit/dom/window_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The window extensions tests 3 | * 4 | * @copyright 2009-2010 Nikolay 5 | */ 6 | var WindowTest = TestCase.create({ 7 | name: 'WindowTest', 8 | 9 | beforeAll: function() { 10 | this.__pos = $(window).scrolls(); 11 | 12 | this.stretcher = $E('div').insertTo(document.body).setStyle({ 13 | width: '10px', height: '2000px' 14 | }); 15 | }, 16 | 17 | afterAll: function() { 18 | $(window).scrollTo(this.__pos); 19 | this.stretcher.remove(); 20 | }, 21 | 22 | testInstance: function() { 23 | var win = new Window(window); 24 | 25 | this.assert(win instanceof RightJS.Window); 26 | this.assert(win instanceof RightJS.Wrapper); 27 | this.assertSame(window, win._); 28 | 29 | var win2 = new Window(window); 30 | this.assertNotSame(win, win2); 31 | this.assertSame(win._, win2._); 32 | }, 33 | 34 | testSize: function() { 35 | var width = window.innerWidth || document.documentElement.clientWidth; 36 | var height = window.innerHeight || document.documentElement.clientHeight; 37 | var size = $(window).size(); 38 | 39 | this.assertEqual(width, size.x); 40 | this.assertEqual(height, size.y); 41 | }, 42 | 43 | testScrolls: function() { 44 | if (Browser.Konqueror) return; 45 | 46 | window.scrollTo(0, 40); 47 | 48 | var scrolls = $(window).scrolls(); 49 | 50 | this.assertEqual(0, scrolls.x); 51 | this.assertEqual(40, scrolls.y); 52 | }, 53 | 54 | testScrollTo: function() { 55 | if (Browser.Konqueror) return; 56 | 57 | this.assertSame($(window), $(window).scrollTo(0, 60)); 58 | 59 | this.assertEqual(60, $(window).scrolls().y); 60 | }, 61 | 62 | testScrollToWithHash: function() { 63 | if (Browser.Konqueror) return; 64 | 65 | this.assertSame($(window), $(window).scrollTo({x: 0, y: 80})); 66 | 67 | this.assertEqual(80, $(window).scrolls().y); 68 | }, 69 | 70 | testScrollToElement: function() { 71 | if (Browser.Konqueror) return; 72 | 73 | var el = $E('div').setStyle({ 74 | position: 'absolute', 75 | left: '0px', top: '120px' 76 | }).insertTo(document.body); 77 | 78 | $(window).scrollTo(el); 79 | 80 | if (!Browser.WebKit) 81 | this.assertEqual(el.position().y, $(window).scrolls().y); 82 | 83 | el.remove(); 84 | }, 85 | 86 | testShortcuts: function() { 87 | this.assertNotNull($(window).onBlur, "test onBlur"); 88 | this.assertNotNull($(window).onFocus, "test onFocus"); 89 | this.assertNotNull($(window).onScroll, "test onScroll"); 90 | this.assertNotNull($(window).onResize, "test onResize"); 91 | } 92 | }); 93 | -------------------------------------------------------------------------------- /test/unit/dom/wrapper_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Testing basic dom-wrapper functionality in here 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | var WrapperTest = TestCase.create({ 7 | name: 'WrapperTest', 8 | 9 | testInstanceCaching: function() { 10 | var div = document.createElement('div'); 11 | var el1 = new Element(div); 12 | var el2 = new Element(div); 13 | 14 | this.assertNotSame(el1, el2); 15 | this.assertSame(el1._, el2._); 16 | this.assertInstanceOf(RightJS.Element, el1); 17 | this.assertInstanceOf(RightJS.Element, el2); 18 | }, 19 | 20 | testInstanceFormTypeCasting: function() { 21 | var form = new Element('form', { 22 | on: { 23 | submit: function() {} 24 | } 25 | }); 26 | 27 | this.assert(form instanceof RightJS.Form); 28 | this.assert(form instanceof RightJS.Element); 29 | this.assert(form instanceof RightJS.Wrapper); 30 | 31 | this.assert(form.observes('submit')); 32 | }, 33 | 34 | testInstanceFormElementTypeCasting: function() { 35 | var input = new Element('input', { 36 | on: { 37 | change: function() {} 38 | } 39 | }); 40 | 41 | this.assert(input instanceof RightJS.Input); 42 | this.assert(input instanceof RightJS.Element); 43 | this.assert(input instanceof RightJS.Wrapper); 44 | 45 | this.assert(input.observes('change')); 46 | }, 47 | 48 | testPrivateWrapper: function() { 49 | var MyElement = new Class(Element, { 50 | initialize: function(element, options) { 51 | this.$super(element, options); 52 | this.boo = 'hoo'; 53 | } 54 | }); 55 | 56 | var my_div = new MyElement('div', {id: 'my-div'}); 57 | 58 | // testing the instance 59 | this.assert(my_div instanceof MyElement); 60 | this.assert(my_div instanceof Element); 61 | 62 | // testing the attributes 63 | this.assertEqual('DIV', my_div._.tagName); 64 | this.assertEqual('my-div', my_div._.id); 65 | this.assertEqual('hoo', my_div.boo); 66 | 67 | this.assertSame(my_div, $(my_div._), "Checking the caching is working"); 68 | }, 69 | 70 | testAnotherPrivateWrapper: function() { 71 | var MyElement = new Class(Element, { 72 | initialize: function(element_id) { 73 | this.$super('div', { 74 | id: element_id, 75 | 'class': 'boo' 76 | }); 77 | 78 | this.addClass('hoo'); 79 | this.onClick('doo'); 80 | } 81 | }); 82 | 83 | var my_div = new MyElement('my-div'); 84 | 85 | this.assert(my_div instanceof MyElement); 86 | this.assert(my_div instanceof Element); 87 | 88 | this.assertEqual('DIV', my_div._.tagName); 89 | this.assertEqual('my-div', my_div._.id); 90 | this.assertEqual('boo hoo', my_div._.className); 91 | 92 | this.assert(my_div.observes('click', 'doo')); 93 | }, 94 | 95 | testPrivateWrapperOverTypeCastedUnits: function() { 96 | var Textarea = new Class(Input, { 97 | initialize: function(options) { 98 | this.$super(Object.merge(options, { 99 | type: 'textarea' 100 | })); 101 | 102 | this.addClass('my-area'); 103 | } 104 | }); 105 | 106 | var txt = new Textarea(); 107 | 108 | this.assert(txt instanceof Textarea); 109 | this.assert(txt instanceof Input); 110 | this.assert(txt instanceof Element); 111 | 112 | this.assertEqual('TEXTAREA', txt._.tagName); 113 | this.assertEqual('my-area', txt._.className); 114 | 115 | this.assertSame(txt, $(txt._), "Checking the caching is working"); 116 | 117 | this.assertFalse(new Element('textarea') instanceof Textarea, 118 | "private wrappers should not get involved in the typecasting"); 119 | }, 120 | 121 | testPrivateWrapperWithMoreThanTwoArguments: function() { 122 | var MyElement = new Class(Element, { 123 | initialize: function(p1, p2, p3, p4) { 124 | this.p1 = p1; 125 | this.p2 = p2; 126 | this.p3 = p3; 127 | this.p4 = p4; 128 | 129 | this.$super('div', {id: 'boo-hoo'}); 130 | } 131 | }); 132 | 133 | var el = new MyElement('1', '2', '3', '4'); 134 | 135 | this.assert(el instanceof Element); 136 | 137 | this.assertEqual('1', el.p1); 138 | this.assertEqual('2', el.p2); 139 | this.assertEqual('3', el.p3); 140 | this.assertEqual('4', el.p4); 141 | 142 | this.assertEqual("DIV", el._.tagName); 143 | this.assertEqual('boo-hoo', el._.id); 144 | }, 145 | 146 | testWithInjections: function() { 147 | var MyElement = new Class(Element, { 148 | include: [{boo: 'hoo'}], 149 | extend: { 150 | BOO: 'HOO' 151 | } 152 | }); 153 | 154 | this.assertEqual('hoo', MyElement.prototype.boo); 155 | this.assertEqual('HOO', MyElement.BOO); 156 | }, 157 | 158 | testWithPrebinds: function() { 159 | var MyElement = new Class(Element, { 160 | prebind: ['test1', 'test2'], 161 | 162 | test1: function() { 163 | this.name = 'test1'; 164 | }, 165 | 166 | test2: function() { 167 | this.name = 'test2'; 168 | } 169 | }); 170 | 171 | var el = new MyElement('div'); 172 | var test1 = el.test1; 173 | var test2 = el.test2; 174 | 175 | test1(); 176 | this.assertEqual('test1', el.name); 177 | 178 | test2(); 179 | this.assertEqual('test2', el.name); 180 | }, 181 | 182 | testCaching: function() { 183 | var element = new Element('div', {id: 'my-div'}).insertTo(document.body); 184 | 185 | this.assertSame($(element._), element); 186 | this.assertSame($('my-div'), element); 187 | 188 | var Div = new Class(Element, {}); 189 | var div = new Div(element._).insertTo(document.body); 190 | 191 | this.assertSame($(div._), div); 192 | this.assertSame($('my-div'), div); 193 | } 194 | }); -------------------------------------------------------------------------------- /test/unit/fx/fx/morph_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Fx.Style unit tests 3 | * 4 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 5 | */ 6 | if (Fx.Options.engine === 'javascript') { 7 | var FxMorphTest = TestCase.create({ 8 | name: 'FxMorphTest', 9 | 10 | setup: function() { 11 | this.el = $E('div').insertTo(document.body); 12 | this.fx = new Fx.Morph(this.el); 13 | this.fx.startTimer = function() { return this; }; 14 | }, 15 | 16 | tearDown: function() { 17 | this.el.remove(); 18 | }, 19 | 20 | testStylesCalculation: function() { 21 | this.fx.start(); 22 | 23 | this.assertEqual({}, this.fx.before); 24 | this.assertEqual({}, this.fx.after); 25 | 26 | this.fx.start({ 27 | height: '100px', 28 | color: '#FFF' 29 | }); 30 | 31 | this.assertEqual([0], this.fx.before.height); 32 | this.assertEqual([0,0,0], this.fx.before.color); 33 | 34 | this.assertEqual([100], this.fx.after.height); 35 | this.assertEqual([255,255,255], this.fx.after.color); 36 | 37 | this.fx.start({ 38 | position: 'relative', 39 | left: '-100px' 40 | }); 41 | 42 | this.assertEqual([-100], this.fx.after.left); 43 | this.assertEqual([0], this.fx.before.left); 44 | }, 45 | 46 | testNamedColorsRecognition: function() { 47 | this.el._.style.color = 'black'; 48 | this.fx.start({ 49 | color: 'yellow' 50 | }); 51 | 52 | this.assertEqual([0,0,0], this.fx.before.color); 53 | this.assertEqual([255, 255, 0], this.fx.after.color); 54 | }, 55 | 56 | testDashedKeysHandling: function() { 57 | this.el._.style.backgroundColor = '#FFF'; 58 | this.fx.start({'background-color': '#DDD'}); 59 | 60 | this.assertEqual([255, 255, 255], this.fx.before.backgroundColor); 61 | this.assertEqual([221, 221, 221], this.fx.after.backgroundColor); 62 | }, 63 | 64 | testBorderSettingUp: function() { 65 | this.fx.start({border: '10px solid green'}); 66 | 67 | this.assertEqual('0px', this.el.getStyle('borderTopWidth')); 68 | this.assertEqual('solid', this.el.getStyle('borderTopStyle')); 69 | }, 70 | 71 | testOpacityProcessing: function() { 72 | this.fx.start({opacity: 0.5}); 73 | 74 | if (Browser.IE8L) { 75 | this.assertEqual(['filter'], Object.keys(this.fx.before)); 76 | this.assertEqual(['filter'], Object.keys(this.fx.after)); 77 | 78 | this.assertEqual([100], this.fx.before.filter); 79 | this.assertEqual([50], this.fx.after.filter); 80 | } else { 81 | this.assertEqual(['opacity'], Object.keys(this.fx.before)); 82 | this.assertEqual(['opacity'], Object.keys(this.fx.after)); 83 | 84 | this.assertEqual([1], this.fx.before.opacity); 85 | this.assertEqual([0.5], this.fx.after.opacity); 86 | } 87 | } 88 | }); 89 | } -------------------------------------------------------------------------------- /test/unit/fx/fx_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Fx unit tests 3 | * 4 | * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. 5 | */ 6 | var FxTest = TestCase.create({ 7 | name: 'FxTest', 8 | 9 | testInstanceDefaults: function() { 10 | var fx = new Fx(); 11 | 12 | this.assertEqual(Fx.Options.fps, fx.options.fps); 13 | this.assertEqual('normal', fx.options.duration); 14 | this.assertEqual('default', fx.options.transition); 15 | }, 16 | 17 | testInstanceWithOptions: function() { 18 | var el = $E('div'); 19 | var fx = new Fx(el, { 20 | fps: 20, 21 | duration: 500, 22 | transition: 'Exp' 23 | }); 24 | 25 | this.assertSame(el, fx.element); 26 | 27 | this.assertEqual(20, fx.options.fps); 28 | this.assertEqual(500, fx.options.duration); 29 | this.assertEqual('Exp', fx.options.transition); 30 | }, 31 | 32 | testTransitions: function() { 33 | for (var name in Fx.Transitions) { 34 | for (var i=0; i <= 1; i+= 0.02) 35 | this.assert(Fx.Transitions[name](i) <= 1, "Assert '"+name+"' for "+i); 36 | } 37 | } 38 | }) 39 | -------------------------------------------------------------------------------- /test/unit/lang/function_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Function class unit-test 3 | * 4 | * Copyright (C) 2008 Nikolay V. Nemshilov aka St. 5 | */ 6 | var FunctionTest = TestCase.create({ 7 | name: 'FunctionTest', 8 | 9 | testBind: function() { 10 | var obj = { 11 | attr: null 12 | }; 13 | 14 | var func = (function() { 15 | return this.attr = 'bind'; 16 | }).bind(obj); 17 | 18 | this.assertInstanceOf(Function, func); 19 | this.assertEqual('bind', func()); 20 | this.assertEqual('bind', obj.attr); 21 | }, 22 | 23 | testBindWithCurry: function() { 24 | var obj = { 25 | attr: null 26 | }; 27 | 28 | var func = (function(def, users) { 29 | return this.attr = users || def; 30 | }).bind(obj, 'default'); 31 | 32 | this.assertInstanceOf(Function, func); 33 | this.assertEqual('default', func()); 34 | this.assertEqual('default', obj.attr); 35 | 36 | this.assertEqual('users', func('users')); 37 | this.assertEqual('users', obj.attr); 38 | }, 39 | 40 | testBindAsEventListener: function() { 41 | var obj = { 42 | 'event': null, 43 | 'attr': null 44 | }; 45 | 46 | var func = (function(event, def, users) { 47 | this.event = event; 48 | return this.attr = def || users; 49 | }).bindAsEventListener(obj, 'default'); 50 | 51 | this.assertInstanceOf(Function, func); 52 | this.assertEqual('default', func('event')); 53 | this.assertEqual('default', obj.attr); 54 | }, 55 | 56 | testCurry: function() { 57 | var func = function(x,y) { return x * y; }; 58 | var triple = func.curry(3); 59 | var quadro = func.curry(4); 60 | 61 | this.assertInstanceOf(Function, triple); 62 | this.assertInstanceOf(Function, quadro); 63 | this.assertEqual(15, triple(5)); 64 | this.assertEqual(20, quadro(5)); 65 | }, 66 | 67 | testRCurry: function() { 68 | var func = function() { 69 | return $A(arguments); 70 | }; 71 | var f1 = func.rcurry(1); 72 | var f2 = func.rcurry(1,2,3); 73 | 74 | this.assertEqual([1,1], f1(1)); 75 | this.assertEqual([1,2,1], f1(1,2)); 76 | this.assertEqual([1,2,1,2,3], f2(1,2)); 77 | }, 78 | 79 | testDelay: function() { 80 | var timeout = (function(txt) { 81 | //alert(txt); 82 | }).delay(5000, 'some text'); 83 | 84 | this.assertNotNull(timeout); 85 | }, 86 | 87 | testPeriodical: function() { 88 | var interval = (function(txt) { 89 | //alert(txt); 90 | }).periodical(5000, 'some text'); 91 | 92 | this.assertNotNull(interval); 93 | }, 94 | 95 | testChain: function() { 96 | var f1 = function(a,num) { a.push(num); return a; }; 97 | var f2 = function(a,num) { a.push(num) }; 98 | var f3 = function(a,num) { a.push(num) }; 99 | var f4 = function(a,num) { a.push(num) }; 100 | 101 | var a = []; 102 | 103 | var f = f1.chain(f2, a, 2).chain(f3, a, 3).chain(f4, a, 4); 104 | 105 | this.assertNotSame(f, f1); 106 | this.assertNotSame(f, f2); 107 | this.assertNotSame(f, f3); 108 | this.assertNotSame(f, f4); 109 | 110 | var r = f(a, 1); 111 | this.assertSame(r, a, "checking it had returned the original result"); 112 | this.assertEqual([1,2,3,4], a, "checking the chain works properly"); 113 | } 114 | }); 115 | -------------------------------------------------------------------------------- /test/unit/lang/json_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The JSON export/import functionality test 3 | * 4 | * @copyright (C) 2009-2011 Nikolay Nemshilov 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.stringify({a:'a1',b:'b1'})); 61 | 62 | this.assertEqual( 63 | '{"a":[1,{"b":1}],"b":1,"c":false,"d":null,"e":{"f":"g"}}', 64 | JSON.stringify({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.parse('{"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.parse('{123'); 78 | }) 79 | } 80 | }); -------------------------------------------------------------------------------- /test/unit/lang/math_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Math unit test-case 3 | * 4 | * Copyright (C) 2008 Nikolay V. Nemshilov aka St. 5 | */ 6 | var MathTest = TestCase.create({ 7 | name: 'MathTest', 8 | 9 | testRandomOriginal: function() { 10 | var fine = true; 11 | for (var i=0; i < 100; i++) { 12 | var rand = Math.random(); 13 | fine = fine && rand > 0 && rand < 1; 14 | } 15 | this.assert(fine); 16 | }, 17 | 18 | testRandomIntegerWithMax: function() { 19 | var rands = []; 20 | for (var i=0; i < 100; i++) { 21 | var rand = Math.random(2); 22 | rands[rand] = rand; 23 | } 24 | this.assertEqual([0, 1, 2], rands); 25 | }, 26 | 27 | testRandomIntegerWithMinMax: function() { 28 | var rands = [null, null]; 29 | for (var i=0; i < 100; i++) { 30 | var rand = Math.random(2,6); 31 | rands[rand] = rand; 32 | } 33 | this.assertEqual([null,null,2,3,4,5,6], rands); 34 | }, 35 | 36 | testRandomWithStringArgs: function() { 37 | var rands = [null, null]; 38 | for (var i=0; i < 100; i++) { 39 | var rand = Math.random('2','4'); 40 | rands[rand] = rand; 41 | } 42 | this.assertEqual([null,null,2,3,4], rands); 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /test/unit/lang/number_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Number class extentions unit-test 3 | * 4 | * Copyright (C) 2008-2011 Nikolay V. Nemshilov 5 | */ 6 | var NumberTest = TestCase.create({ 7 | name: 'NumberTest', 8 | 9 | testTimes: function() { 10 | var four = 4, times = 0; 11 | 12 | this.assertSame(four, four.times(function() { times ++; })); 13 | this.assertEqual(4, times); 14 | }, 15 | 16 | testTimesWithScope: function() { 17 | this.list = []; 18 | 19 | (4).times(function(i) { 20 | this.list.push(i); 21 | }, this); 22 | 23 | this.assertEqual([0,1,2,3], this.list); 24 | }, 25 | 26 | testUpto: function() { 27 | this.list = []; 28 | 29 | (2).upto(8, function(i) { 30 | this.list.push(i * 2); 31 | }, this); 32 | 33 | this.assertEqual([4,6,8,10,12,14,16], this.list); 34 | }, 35 | 36 | testDownto: function() { 37 | this.list = []; 38 | 39 | (8).downto(4, function(i) { 40 | this.list.push(i / 2); 41 | }, this); 42 | 43 | this.assertEqual([4,3.5,3,2.5,2], this.list); 44 | }, 45 | 46 | testTo: function() { 47 | this.assertEqual([1,2,3,4], 1..to(4)); 48 | this.assertEqual([4,3,2,1], 4..to(1)); 49 | 50 | this.assertEqual([2,4,6,8], 1..to(4, function(i) { return i * 2; })); 51 | }, 52 | 53 | testAbs: function() { 54 | this.assert((-4).abs() == 4); 55 | }, 56 | 57 | testRound: function() { 58 | this.assert((4.4).round() == 4); 59 | this.assert((4.6).round() == 5); 60 | }, 61 | 62 | testRoundWithOption: function() { 63 | this.assert(4.444.round(1) == 4.4); 64 | this.assert(4.444.round(2) == 4.44); 65 | this.assert(4.444.round(3) == 4.444); 66 | }, 67 | 68 | testCeil: function() { 69 | this.assert((4.4).ceil() == 5); 70 | }, 71 | 72 | testFloor: function() { 73 | this.assert((4.6).floor() == 4); 74 | }, 75 | 76 | testMin: function() { 77 | this.assertEqual(2.2, 2.2.min(2.1)); 78 | this.assertEqual(2.2, 2.1.min(2.2)); 79 | }, 80 | 81 | testMax: function() { 82 | this.assertEqual(2.2, 2.2.max(2.4)); 83 | this.assertEqual(2.2, 2.4.max(2.2)); 84 | } 85 | }); 86 | -------------------------------------------------------------------------------- /test/unit/lang/object_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Object unit test-case 3 | * 4 | * Copyright (C) 2008-2011 Nikolay Nemshilov 5 | */ 6 | var ObjectTest = TestCase.create({ 7 | name: 'ObjectTest', 8 | 9 | testKeys: function() { 10 | this.assertEqual(['a', 'b'], Object.keys({a:1, b:2})); 11 | }, 12 | 13 | testValues: function() { 14 | this.assertEqual([1,2], Object.values({a:1, b:2})); 15 | }, 16 | 17 | testEach: function() { 18 | var hash = {k1: 'v1', k2: 'v2'}, args = [], scope = {}, callback_scope; 19 | 20 | this.assertSame(hash, Object.each(hash, function() { 21 | args.push($A(arguments)); 22 | callback_scope = this; 23 | }, scope)); 24 | 25 | this.assertSame(scope, callback_scope); 26 | this.assertEqual([ 27 | ['k1', 'v1'], 28 | ['k2', 'v2'] 29 | ], args); 30 | }, 31 | 32 | testEmpty: function() { 33 | this.assert(Object.empty({})); 34 | this.assertFalse(Object.empty({1: 1})); 35 | 36 | this.assertFalse(Object.empty({'': 'boo'}), "should be fine with weird keys"); 37 | }, 38 | 39 | testWithout: function() { 40 | this.assertEqual({b:2, c:3}, Object.without({a:1, b:2, c:3}, 'a')); 41 | this.assertEqual({c:3}, Object.without({a:1, b:2, c:3}, 'a', 'b')); 42 | }, 43 | 44 | testOnly: function() { 45 | this.assertEqual({b:2,d:4}, Object.only({a:1, b:2, c:3, d:4}, 'b', 'd')); 46 | }, 47 | 48 | testMerge: function() { 49 | var o1 = {1:1}; 50 | var o2 = {2:2}; 51 | var o3 = {3:3}; 52 | var o4 = Object.merge(o1,o2,o3,null,false,1); 53 | 54 | this.assertEqual({1:1,2:2,3:3}, o4); 55 | this.assertEqual({1:1}, o1); 56 | this.assertEqual({2:2}, o2); 57 | this.assertEqual({3:3}, o3); 58 | }, 59 | 60 | testDeepMerge: function() { 61 | var o1 = {a: {b: {c: 'd'}, e: 'f'}}; 62 | var o2 = {a: {b: {c: 'd', e: 'f'}}}; 63 | 64 | var o = Object.merge(o1, o2); 65 | 66 | this.assertEqual( 67 | {a: {b: {c: 'd', e: 'f'}, e: 'f'}}, o, 68 | "getting sure it was actually merged" 69 | ); 70 | this.assert( 71 | o.a !== o1.a && o.a !== o2.a && o.a.b !== o1.a.b && o.a.b !== o2.a.b, 72 | "checking that all the keys were delinked" 73 | ); 74 | }, 75 | 76 | testThreeWayDeepMerge: function() { 77 | var o1 = {a: {a1: 1}}; 78 | var o2 = {b: {b1: 2}}; 79 | var o3 = {a: {a1: 2, a2: 3}}; 80 | 81 | var o = Object.merge(o1, o2, o3); 82 | 83 | this.assertEqual( 84 | {a: {a1: 2, a2: 3}, b: {b1: 2}}, o, 85 | "should do a deep merge with overriding" 86 | ); 87 | }, 88 | 89 | testClone: function() { 90 | var o1 = {1:1}; 91 | var o2 = Object.clone(o1); 92 | 93 | this.assertEqual(o1, o2); 94 | this.assertNotSame(o1, o2); 95 | }, 96 | 97 | testToQueryString: function() { 98 | this.assertEqual('a=a&b=b&c=%25%23%3F', Object.toQueryString({a:'a', b:'b', c:'%#?'})); 99 | this.assertEqual('a%5B%5D=1&a%5B%5D=2&a%5B%5D=3', Object.toQueryString({'a[]': [1,2,3]})); 100 | this.assertEqual('a%5Bb%5D%5Bc%5D=1&a%5Bd%5D=2', Object.toQueryString({a: {b: {c: 1}, d: 2}})); 101 | } 102 | }); 103 | -------------------------------------------------------------------------------- /test/unit/lang/regexp_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * the Regexp unit tests 3 | * 4 | * Copyright (C) 2008-2009 Nikolay V. Nemshilov aka St. 5 | */ 6 | var RegexpTest = TestCase.create({ 7 | name: 'RegexpTest', 8 | 9 | testEscape: function() { 10 | this.assertEqual('\\.\\*\\+\\?\\^\\=\\!\\:\\$\\{\\}\\(\\)\\|\\[\\]\\/\\\\', RegExp.escape(".*+?^=!:${}()|[]/\\")); 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /test/unit/lang/string_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The String class unit-test 3 | * 4 | * Copyright (C) 2008-2010 Nikolay V. Nemshilov aka St. 5 | */ 6 | var StringTest = TestCase.create({ 7 | name: 'StringTest', 8 | 9 | testEmpty: function() { 10 | this.assert(''.empty()); 11 | this.assertFalse(' '.empty()); 12 | }, 13 | 14 | testBlank: function() { 15 | this.assert(''.blank()); 16 | this.assert(" \t\n ".blank()); 17 | this.assertFalse(' s'.blank()); 18 | }, 19 | 20 | testTrim: function() { 21 | this.assert('asdf', " \t\nasdf\t\n\r \n".trim()); 22 | this.assert('фыва', " \t\nфыва\t\n\r \n".trim()); 23 | }, 24 | 25 | testStripTags: function() { 26 | this.assertEqual('asdf фыва asdf', 'asdf фыва
asdf'.stripTags()); 27 | }, 28 | 29 | testStripScripts: function() { 30 | this.assertEqual('asdf asdf', "asdf asdf".stripScripts()); 31 | }, 32 | 33 | testStripScriptsWithScriptsEval: function() { 34 | self.______called______ = false; 35 | "asdf ".stripScripts(true); 36 | this.assert(self.______called______); 37 | self.______called______ = undefined; 38 | }, 39 | 40 | testStripScriptsWithScriptsExtraction: function() { 41 | var scripts = ''; 42 | " asdf ".stripScripts( 43 | function(source) { scripts = source; } 44 | ); 45 | this.assertEqual("alert('bla');\nalert('foo');", scripts.trim()); 46 | }, 47 | 48 | testExtractScripts: function() { 49 | this.assertEqual( 50 | "alert('bla');\nalert('foo');", 51 | " asdf ".extractScripts().trim() 52 | ); 53 | }, 54 | 55 | testEvalScripts: function() { 56 | self.______called______ = false; 57 | "asdf ".evalScripts(); 58 | this.assert(self.______called______); 59 | self.______called______ = undefined; 60 | }, 61 | 62 | testCamelize: function() { 63 | this.assertEqual('asdfAsdfAsdf', 'asdf_asdf_asdf'.camelize()); 64 | this.assertEqual('asdfAsdfAsdf', 'asdf-asdf-asdf'.camelize()); 65 | this.assertEqual('AsdfAsdfASDf', '_asdf_asdf_ASDf'.camelize()); 66 | }, 67 | 68 | testUnderscored: function() { 69 | this.assertEqual('asdf_asdf_asdf', 'asdfAsdfAsdf'.underscored()); 70 | this.assertEqual('asdf_asdf_asdf', 'asdfAsdfASDF'.underscored()); 71 | this.assertEqual('asdf_asdf_asdf', 'asdf-asdf-asdf'.underscored()); 72 | }, 73 | 74 | testCapitalize: function() { 75 | this.assertEqual('Asdf', 'asdf'.capitalize()); 76 | this.assertEqual('Asdf asdf', 'asdf asdf'.capitalize()); 77 | this.assertEqual('Asdf-asdf', 'asdf-asdf'.capitalize()); 78 | this.assertEqual('Мама мыла раму', 'мама мыла раму'.capitalize()); 79 | this.assertEqual('Мама-мыла раму', 'мама-мыла раму'.capitalize()); 80 | }, 81 | 82 | testDasherize: function() { 83 | this.assertEqual('asdf-asdf', 'asdfAsdf'.dasherize()); 84 | this.assertEqual('asdf-asdf', 'asdf_asdf'.dasherize()); 85 | }, 86 | 87 | testIncludes: function() { 88 | this.assert('asdf sdfg dfg'.includes('sdf')); 89 | this.assertFalse('asdf sdfg df'.includes('qwer')); 90 | }, 91 | 92 | testStartsWith: function() { 93 | this.assert('asdf sdf'.startsWith('asdf')); 94 | this.assertFalse('asdf sdf'.startsWith('Asdf')); 95 | 96 | this.assert('asdf sdf'.startsWith('Asdf', true), 'test checking with ignore case'); 97 | }, 98 | 99 | testEndsWith: function() { 100 | this.assert('asdf qwer'.endsWith('qwer')); 101 | this.assertFalse('asdf qwer'.endsWith('Qwer')); 102 | this.assertFalse('asdf'.endsWith('qwert')); 103 | 104 | this.assert('asdf qwer'.endsWith('Qwer', true), 'test checking with ignore case'); 105 | }, 106 | 107 | testToInt: function() { 108 | this.assertEqual(1, '1'.toInt()); 109 | this.assertEqual(2, '2.2'.toInt()); 110 | this.assert(isNaN('asdf'.toInt())); 111 | }, 112 | 113 | testToFloat: function() { 114 | this.assertEqual(1.0, '1'.toFloat()); 115 | this.assertEqual(2.2, '2.2'.toFloat()); 116 | this.assertEqual(3.3, '3,3'.toFloat()); 117 | this.assertEqual(4.4, '4-4'.toFloat()); 118 | this.assertEqual(-4.4, '-4-4'.toFloat()); 119 | this.assertEqual(4.0, '4-4'.toFloat(true)); 120 | this.assert(isNaN('asdf'.toFloat())); 121 | }, 122 | 123 | testToHex: function() { 124 | this.assertEqual('#FFFFFF', '#FFF'.toHex()); 125 | this.assertEqual('#AABB88', '#AB8'.toHex()); 126 | this.assertEqual('#ffffff', 'rgb(255,255,255)'.toHex()); 127 | this.assertEqual('#000000', 'rgb(0, 0, 0)'.toHex()); 128 | }, 129 | 130 | testToRgb: function() { 131 | this.assertEqual('rgb(255,255,255)', '#FFF'.toRgb()); 132 | this.assertEqual('rgb(0,0,0)', '#000000'.toRgb()); 133 | 134 | this.assertEqual([170, 187, 204], '#ABC'.toRgb(true)); 135 | } 136 | }); 137 | -------------------------------------------------------------------------------- /test/unit/xhr/xhr/iframed_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Xhr.IFramed unit tests 3 | * 4 | * Copyright (C) 2009 Nikolay V. Nemshilov aka St. 5 | */ 6 | var XhrIFramedTest = TestCase.create({ 7 | name: 'XhrIFramedTest', 8 | 9 | setUp: function() { 10 | this.form = new Form().insertTo(document.body); 11 | }, 12 | 13 | tearDown: function() { 14 | this.form.remove(); 15 | }, 16 | 17 | testInstance: function() { 18 | var form = this.form; 19 | var ixhr = new Xhr.IFramed(form); 20 | var iframe = $(ixhr.id); 21 | 22 | this.assertSame(form, ixhr.form); 23 | this.assertEqual('IFRAME', iframe._.tagName); 24 | 25 | this.assertEqual('about:blank', iframe.get('src')); 26 | this.assertEqual('0', iframe.get('width')); 27 | this.assertEqual('0', iframe.get('height')); 28 | // FIXME IE6 doesn't get it 29 | //this.assertEqual('0', ixhr.iframe.get('frameborder')); 30 | 31 | //this.assert(ixhr.iframe.onload); 32 | 33 | this.assertTypeOf('function', ixhr.send); 34 | this.assertTypeOf('function', ixhr.open); 35 | this.assertTypeOf('function', ixhr.abort); 36 | this.assertTypeOf('function', ixhr.setRequestHeader); 37 | }, 38 | 39 | testOnLoad: function() { 40 | var form = this.form; 41 | var ixhr = new Xhr.IFramed(form); 42 | 43 | var onready_called = false; 44 | ixhr.onreadystatechange = function() { onready_called = true; }; 45 | 46 | ixhr.onLoad(); 47 | 48 | this.assert(onready_called); 49 | this.assertEqual(4, ixhr.readyState); 50 | this.assertEqual(200, ixhr.status); 51 | }, 52 | 53 | testXhrFormHookForSimpleForm: function() { 54 | var xhr = new Xhr('/boo'); 55 | 56 | xhr.form = this.form; 57 | 58 | this.assert(xhr.createXhr().constructor !== Xhr.IFramed); 59 | }, 60 | 61 | testXhrFormHookForFormWithFiles: function() { 62 | var xhr = new Xhr('/boo'); 63 | 64 | xhr.form = this.form.insert(''); 65 | 66 | this.assert(xhr.createXhr().constructor === Xhr.IFramed); 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /test/unit/xhr/xhr/jsonp_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The Xhr.JSONP interface test 3 | * 4 | * Copyright (C) 2010 Nikolay Nemshilov 5 | */ 6 | var XhrJSONPTest = TestCase.create({ 7 | name: 'XhrJSONPTest', 8 | 9 | testInitialize: function() { 10 | var xhr = new Xhr.JSONP({ 11 | encoding: 'utf-8', 12 | jsonp: true, 13 | async: true 14 | }); 15 | 16 | this.assert(xhr.script); 17 | this.assertEqual('SCRIPT', xhr.script._.tagName); 18 | this.assertEqual('utf-8', xhr.script._.charset); 19 | this.assert(xhr.script._.async); 20 | 21 | this.assert(xhr.name.startsWith(xhr.prefix)); 22 | this.assert(xhr.name != xhr.prefix); 23 | 24 | this.assertEqual('callback='+xhr.name, xhr.param); 25 | }, 26 | 27 | testInitializeWithOptions: function() { 28 | var xhr = new Xhr.JSONP({ 29 | encoding: 'cp1251', 30 | jsonp: 'some_name', 31 | async: false 32 | }); 33 | 34 | this.assertEqual('cp1251', xhr.script._.charset); 35 | this.assertFalse(xhr.script._.async); 36 | this.assertEqual('some_name='+xhr.name, xhr.param); 37 | }, 38 | 39 | testSend: function() { 40 | var options = { 41 | jsonp: true, 42 | async: true 43 | }; 44 | 45 | var url = document.location.href.replace(/\/[^\/]+$/, '') + "/some.url"; 46 | 47 | var xhr = new Xhr.JSONP(options); 48 | xhr.open('get', url, true); 49 | xhr.send('some=data'); 50 | 51 | this.assertEqual( 52 | url + "?" + xhr.param +"&some=data", 53 | xhr.script._.src 54 | ); 55 | 56 | // checking the global callback presence 57 | this.assert(typeof(window[xhr.name]) == 'function'); 58 | }, 59 | 60 | testFinish: function() { 61 | var options = { 62 | jsonp: true, 63 | async: true 64 | }; 65 | 66 | var xhr = new Xhr.JSONP(options); 67 | xhr.open('get', './some.url', true); 68 | xhr.send('some=data'); 69 | xhr.onreadystatechange = function() { 70 | options.called_back = true; 71 | }; 72 | 73 | // emulating the callback 74 | window[xhr.name]({some: 'data'}); 75 | 76 | // checking the xhr data receiving 77 | this.assertEqual({some: 'data'}, options.json); 78 | this.assertSame(options.json, options.responseJSON); 79 | 80 | this.assertEqual(200, xhr.status); 81 | this.assertEqual(4, xhr.readyState); 82 | 83 | this.assert(options.called_back); 84 | } 85 | 86 | }); 87 | -------------------------------------------------------------------------------- /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 | ] 26 | }, 27 | 28 | /** 29 | * Basic constructor 30 | * 31 | * @param {String} the source 32 | * @param {String} the linter options 33 | * @return void 34 | */ 35 | initialize: function(src, options) { 36 | this.source = src; 37 | this.options = options; 38 | }, 39 | 40 | /** 41 | * Runs the linter 42 | * 43 | * @return {Linter} this 44 | */ 45 | run: function() { 46 | var options = {}, okays = []; 47 | 48 | // extracting the additional options 49 | eval(fs.readFileSync(this.options).toString()); 50 | 51 | JSLint.okays = this.constructor.Okays.concat(okays); 52 | 53 | JSLint( 54 | fs.readFileSync(this.source).toString(), 55 | Object.merge(this.constructor.Options, options) 56 | ); 57 | 58 | this.errors = JSLint.errors.compact(); 59 | 60 | return this; 61 | }, 62 | 63 | /** 64 | * Prints out the check report 65 | * 66 | * @return {Linter} this 67 | */ 68 | report: function() { 69 | 70 | if (this.errors.empty()) { 71 | console.log("\u001B[32m - JSLint check successfully passed\u001B[0m"); 72 | } else { 73 | console.log("\u001B[31m - JSLint check failed in: "+ this.source + "\u001B[0m"); 74 | 75 | this.errors.each(function(error) { 76 | var report = "\n", j=0, pointer=''; 77 | for (; j < error.character-1; j++) { pointer += '-'; } 78 | 79 | report += " \u001B[35m"+ error.reason +"\u001B[0m "; 80 | 81 | if (error.evidence) { 82 | report += "Line: "+ error.line + ", Char: "+ error.character + "\n"; 83 | report += " "+ error.evidence + "\n"; 84 | report += " \u001B[33m"+ pointer + "^\u001B[0m"; 85 | } 86 | 87 | console.log(report); 88 | }); 89 | 90 | console.log("\n") 91 | } 92 | return this; 93 | } 94 | 95 | }); -------------------------------------------------------------------------------- /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.layout = options.layout || null; 21 | this.header = options.header || null; 22 | this.holders = options.holders || null; 23 | 24 | this.compile(); 25 | }, 26 | 27 | /** 28 | * Runs the Source compilation process 29 | * 30 | * @return {Srouce} this 31 | */ 32 | compile: function() { 33 | // compiling the basic source code 34 | this.source = this.files.map(function(filename) { 35 | return this.read( 36 | filename.endsWith('.js') ? filename : 37 | ('src/' + filename + '.js') 38 | ); 39 | }, this).join("\n\n"); 40 | 41 | // placing everything in a layout 42 | if (this.layout) { 43 | var layout = this.read(this.layout).split('%{source_code}'); 44 | this.source = layout[0] + this.source + layout[1]; 45 | } 46 | 47 | // filling in the placeholders 48 | for (var key in this.holders) { 49 | this.source = this.source 50 | .replace('%{'+ key +'}', this.holders[key]); 51 | } 52 | 53 | // reading the header content 54 | this.header = this.read(this.header); 55 | 56 | // trying to embed the version number into the header 57 | var match = this.source.match(/version\s*(:|=)\s*('|")(.+?)\2/i); 58 | if (match) { 59 | this.header = this.header.replace('%{version}', match[3]); 60 | } 61 | }, 62 | 63 | /** 64 | * An interface to patch the compiled files manually 65 | * 66 | * @param {Function} callback 67 | * @return {Source} this 68 | */ 69 | patch: function(callback) { 70 | this.source = callback.call(this, this.source); 71 | return this; 72 | }, 73 | 74 | /** 75 | * Writes the compiled Source down in the filename 76 | * 77 | * @param {String} filename (relative to the build directory) 78 | * @return {Source} this 79 | */ 80 | write: function(filename) { 81 | if (!filename.includes('-server')) { 82 | filename += '-src'; 83 | } 84 | 85 | this.filename = filename + '.js'; 86 | 87 | fs.writeFileSync(this.filename, this.header + this.source); 88 | return this; 89 | }, 90 | 91 | /** 92 | * Checks this source against with the JSLint 93 | * 94 | * @param {String} lint options file 95 | * @return {Linter} with the results 96 | */ 97 | check: function(lintfile) { 98 | return new Linter(this.filename, lintfile).run().report(); 99 | }, 100 | 101 | /** 102 | * Compresses the source code and writes it down into the file 103 | * 104 | * @return {Source} this 105 | */ 106 | compress: function() { 107 | var jsp = require('./ugly/parse-js'); 108 | var pro = require('./ugly/process'); 109 | var ast = jsp.parse(this.source); 110 | 111 | ast = pro.ast_mangle(ast); 112 | ast = pro.ast_squeeze(ast); 113 | 114 | var filename = this.filename.replace('-src', ''); 115 | 116 | fs.writeFileSync(filename, this.header + pro.gen_code(ast)); 117 | 118 | // making a GZIP to check the compression 119 | try { // ain't gonna work on win 120 | require('child_process').exec( 121 | 'gzip -c ' + filename + ' > '+ filename + '.gz' 122 | ); 123 | } catch(e) {}; 124 | 125 | 126 | return this; 127 | }, 128 | 129 | // protected 130 | 131 | /** 132 | * Shortcut for the Node's crappy fs-read 133 | * 134 | * @param {String} filename 135 | * @return {Strng} file content 136 | */ 137 | read: function(filename) { 138 | return fs.readFileSync(filename).toString(); 139 | } 140 | 141 | }); -------------------------------------------------------------------------------- /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/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 | --------------------------------------------------------------------------------