From 127a8d84e40fa94f9dd8236b9c72c79c1af53327 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Tue, 25 Aug 2009 22:35:21 -0400 Subject: [PATCH] working on javascript layout management --- .../FloatedDivConstructor.js | 73 + .../LayoutConstructorsTest.html | 63 + .../TableLayoutConstructor.js | 3 + addons/Core/layout_constructors/testcase.js | 2391 +++++++++++++++++ addons/Core/partials/layout-editor.inc | 8 + 5 files changed, 2538 insertions(+) create mode 100644 addons/Core/layout_constructors/FloatedDivConstructor.js create mode 100644 addons/Core/layout_constructors/LayoutConstructorsTest.html create mode 100644 addons/Core/layout_constructors/TableLayoutConstructor.js create mode 100644 addons/Core/layout_constructors/testcase.js create mode 100644 addons/Core/partials/layout-editor.inc diff --git a/addons/Core/layout_constructors/FloatedDivConstructor.js b/addons/Core/layout_constructors/FloatedDivConstructor.js new file mode 100644 index 0000000..47787ae --- /dev/null +++ b/addons/Core/layout_constructors/FloatedDivConstructor.js @@ -0,0 +1,73 @@ +var FloatedDivConstructor = Class.create({ + 'generate_html': function(layout) { + var output = []; + output.push('
'); + + var areas = $w("header comic body footer"); + var i, il; + + var indent = 1; + + var range = null; + if (layout.left) { + if (layout.right) { + range[0] = Math.min(layout.left[0], layout.right[0]); + range[1] = Math.max(layout.left[1], layout.right[1]); + } else { + range = layout.left; + } + } else { + if (layout.right) { + range = layout.right; + } + } + + for (i = 0, il = areas.length; i < il; ++i) { + if (range) { + if (range[0] == i) { + output.push(" ".times(indent) + ''); + } + } + } + + output.push('
'); + + return output.join("\n"); + }, + 'generate_css': function(info) { + var output = []; + var include_container = false; + + $w('left right').each(function(field) { + if (info[field]) { + include_container = true; + output.push('#' + field + '-sidebar { float: ' + field + '; display: inline; width: ' + info[field].width + 'px }'); + } + }); + + if (include_container) { + output.unshift('#sidebar-container { overflow: hidden }'); + output.push('.clear { clear: both }'); + } + + return output.join("\n"); + } +}); \ No newline at end of file diff --git a/addons/Core/layout_constructors/LayoutConstructorsTest.html b/addons/Core/layout_constructors/LayoutConstructorsTest.html new file mode 100644 index 0000000..e299ef3 --- /dev/null +++ b/addons/Core/layout_constructors/LayoutConstructorsTest.html @@ -0,0 +1,63 @@ + + + LayoutConstructorsTest + + + + + + + + \ No newline at end of file diff --git a/addons/Core/layout_constructors/TableLayoutConstructor.js b/addons/Core/layout_constructors/TableLayoutConstructor.js new file mode 100644 index 0000000..59b825f --- /dev/null +++ b/addons/Core/layout_constructors/TableLayoutConstructor.js @@ -0,0 +1,3 @@ +var TableLayoutConstructor = Class.create({ + +}); \ No newline at end of file diff --git a/addons/Core/layout_constructors/testcase.js b/addons/Core/layout_constructors/testcase.js new file mode 100644 index 0000000..5a77097 --- /dev/null +++ b/addons/Core/layout_constructors/testcase.js @@ -0,0 +1,2391 @@ +/** + * TestCase - JavaScript testing framework, version 2.0.2 + * Copyright (C) 2007-2008 Nikolay V. Nemshilov aka St. + * + * The library is freely distributable under the terms of an MIT-style license + * For details, see the TestCase web site: http://testcase.rubyforge.net + */ + + +var TestCaseUtil = { + create_class: function() { + var superklass = null, params = this.to_a(arguments); + if (typeof params[0] == 'function') + superklass = params.shift(); + var klass = function() { + return this.initialize ? this.initialize.apply(this, arguments) : this; + } + if (superklass) { + var subclass = function() { }; + subclass.prototype = superklass.prototype; + klass.prototype = new subclass; + klass.superclass = superklass; + } + klass.prototype.constructor = klass; + if (params[0]) + this.extend(klass.prototype, params[0]); + return klass; + }, + extend: function(object, properties, dont_overwrite) { + for (key in properties) + if (!(dont_overwrite && typeof object[key] != 'undefined')) + object[key] = properties[key]; + return object; + }, + extend_with_camelized_aliases: function(object, mixin) { + var camelized_mixin = {}; + for (key in mixin) { + var parts = key.split('_'), camelized = parts[0]; + for (var i=1; i < parts.length; i++) + camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); + camelized_mixin[camelized] = mixin[key]; + } + return TestCaseUtil.extend(object, camelized_mixin); + }, + extend_fully_object_and_prototype: function(object, mixin) { + TestCaseUtil.extend(object, mixin); + TestCaseUtil.extend(object.prototype, mixin); + TestCaseUtil.extend_with_camelized_aliases(object, mixin); + return TestCaseUtil.extend_with_camelized_aliases(object.prototype, mixin); + }, + equal: function(obj1, obj2) { + return TestCaseUtil.to_s(obj1) == TestCaseUtil.to_s(obj2); + }, + to_a: function(it) { + var a = []; + for (var i=0; i < it.length; i++) + a.push(it[i]); + return a; + }, + to_s: function(object, one_level) { + var value = this._try_to_s(object); + if (value !== object) return value; + var entries = this._get_object_stringified_entries( + one_level ? this._one_level_to_s : this.to_s, object + ); + return object instanceof Array ? '['+ entries.join(', ')+ ']' : '{' + entries.join(', ') + '}'; + }, + debug: function(object) { + return this.to_s(object, true); + }, + MAX_OBJECT_STRING_LENGTH: 80, + to_pretty_s: function(object, offset) { + if (object == null) { return 'null'; } + else if (typeof object != 'object') { + return this.to_s(object); + } else { + var offset = offset || 0; + var object_s = this.to_s(object); + if (object_s.length > this.MAX_OBJECT_STRING_LENGTH) { + if (object instanceof Array) { + var len = object.length; + } else { + var len = 0; + for (var key in object) { + len++; + } + } + if (len > 1) { + var entries = this._get_object_stringified_entries( + this.to_pretty_s, object, offset + 1 + ); + var offset_s = ''; + for (var i=0; i < offset; i++) { + offset_s += ' '; + } + object_s = object instanceof Array ? + "[" + entries.join(",\n "+ offset_s) + "\n"+offset_s+"]" : + "{" + entries.join(",\n "+ offset_s) + "\n"+offset_s+"}" ; + } + } + return object_s; + } + }, + _get_object_stringified_entries: function(method, object, param) { + var entries = []; + if (object instanceof Array) { + for (var i=0; i < object.length; i++) { + entries.push(method.apply(this, [object[i], param])); + } + } else { + for (var property in object) { + entries.push(this.to_s(property) + ': ' + method.apply(this, [object[property], param])); + } + } + return entries; + }, + _one_level_to_s: function(object) { + var value = this._try_to_s(object); + if (value !== object) { + return value; + } else if (object instanceof Array) { + return '[...]'; + } else { + return '{...}'; + } + }, + _try_to_s: function(object) { + switch (typeof object) { + case 'undefined': + case 'unknown': return 'undefined'; + case 'string': return '"'+object+'"'; + case 'number': return ''+object; + case 'function': + case 'boolean': return object.toString(); + } + if (object === null) return 'null'; + if (object.nodeType == 1) + return '<'+object.tagName+ + (object.id ? ' id="'+object.id+'"':'')+ + (object.className ? ' class="'+object.className+'"':'')+ + (object.title ? ' title="'+object.title+'"':'')+ + (object.type ? ' type="'+object.type+'"':'')+ + '>'+object.innerHTML+''; + return object; + }, + get_object_class_name: function(object) { + if (object === null) { return 'Null'; } + switch (typeof object) { + case 'string': return 'String'; + case 'boolean': return 'Boolean'; + case 'number': return 'Number'; + case 'function': return 'Function'; + case 'object': + if (typeof object.constructor != 'undefined') { + var match = object.constructor.toString().match(/function\s*(\w+)/); + if (match && match.length == 2) { + return match[1]; + } + for (var key in self) { + try { + if (object instanceof self[key]) + return key; + } catch (e) {} + } + if (object instanceof Array) { + return 'Array'; + } else { + return 'Object'; + } + } + } + return 'Unknown'; + }, + get_class_name: function(klass) { + if (klass === null) { return 'Null'; } + switch (klass) { + case String: return 'String'; + case Boolean: return 'Boolean'; + case Number: return 'Number'; + case Function: return 'Function'; + case Array: return 'Array'; + case Object: return 'Object'; + default: + for (var key in self) { + if (klass == self[key]) + return key; + } + } + return 'Unknown'; + }, + Browser: { + IE: !!(window.attachEvent && !window.opera), + Opera: !!window.opera, + WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, + Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, + MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/), + Konqueror: navigator.userAgent.indexOf('Konqueror') != -1 + }, + $: function(element) { + return typeof element == 'string' ? document.getElementById(element) : element; + }, + $$: function(css_rule) { + return this.Element.select(document, css_rule); + } +}; +/* ------------------------------------------------------------------- */ + +TestCaseUtil.Array = { + include: function(list, item) { + for (var i=0; i < list.length; i++) + if (list[i] == item) + return true; + return false; + }, + merge: function(list1, list2) { + for (var i=0; i < list2.length; i++) { + if (!this.include(list1, list2[i])) + list1.push(list2[i]); + } + return list1; + }, + without: function(list, item) { + var new_list = []; + for (var i=0; i < list.length; i++) + if (list[i] != item) + new_list.push(list[i]); + return new_list; + }, + uniq: function(list) { + var new_list = []; + for (var i=0; i < list.length; i++) + if (!this.include(new_list, list[i])) + new_list.push(list[i]); + return new_list; + }, + find: function(list, callback) { + for (var i=0; i < list.length; i++) + if (callback(list[i])) + return list[i]; + return null; + }, + each: function(list, callback) { + for (var i=0; i < list.length; i++) + callback(list[i]); + return list; + } +}; +/* ------------------------------------------------------------------- */ + +TestCaseUtil.Event = { + KEY_UP: 38, + observe: function(element, name, callback) { + var element = TestCaseUtil.$(element); + if (element.addEventListener) { + element.addEventListener(name, callback, false); + } else { + element.attachEvent("on" + name, callback); + } + return element; + }, + stop: function(event) { + if (event.stopPropagation) { + event.stopPropagation(); + event.preventDefault(); + } else { + event.cancelBubble = true; + event.returnValue = false; + } + }, + element: function(event) { + var target = event.srcElement ? event.srcElement : event.target; + return (!target || target.nodeType == 1) ? target : target.parentNode; + }, + pointerX: function(event) { + return this.pointer(event).x; + }, + pointerY: function(event) { + return this.pointer(event).y; + }, + pointer: function(event) { + return { + x: event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)), + y: event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)) + }; + } +}; +/* ------------------------------------------------------------------- */ + +TestCaseUtil.Element = { + visible: function(element) { + return TestCaseUtil.$(element).style.display != 'none'; + }, + show: function(element) { + TestCaseUtil.$(element).style.display = ''; + }, + hide: function(element) { + TestCaseUtil.$(element).style.display = 'none'; + }, + toggle: function(element) { + if (this.visible(element)) + this.hide(element); + else + this.show(element); + }, + up: function(element, css_rule) { + return this.Selector.match_atom(element, css_rule) ? element : + (element.parentNode ? this.up(element.parentNode, css_rule) : null); + }, + down: function(element, css_rule) { + return this.Selector.find_first(element, css_rule); + }, + select: function(element, css_rule) { + return this.Selector.find_all(element, css_rule); + }, + has_class_names: function(element, class_names) { + for (var i=0; i < class_names.length; i++) + if (!this.has_class_name(element, class_names[i])) + return false; + return true; + }, + has_class_name: function(element, class_name) { + var elementClassName = element.className; + return (elementClassName && elementClassName.length > 0 && (elementClassName == class_name || + new RegExp("(^|\\s)" + class_name + "(\\s|$)").test(elementClassName))); + }, + add_class_name: function(element, class_name) { + if (!this.has_class_name(element, class_name)) + element.className += (element.className ? ' ' : '') + class_name; + return element; + }, + remove_class_name: function(element, class_name) { + element.className = element.className.replace( + new RegExp("(^|\\s+)" + class_name + "(\\s+|$)"), ' ').replace(/^\s+|\s+$/g, ''); + return element; + }, + has_attributes: function(element, attrs) { + for (key in attrs) + if (attrs[key] != null && element[key] != attrs[key]) + return false; + return true; + } +}; +/* ------------------------------------------------------------------- */ + +TestCaseUtil.Element.Selector = { + find_first: function(element, css_rule) { + var founds = this.find_all(element, css_rule); + return founds.length ? founds[0] : null; + }, + find_all: function(element, css_rule) { + var founds = [], element = TestCaseUtil.$(element); + if (typeof css_rule == 'string') + css_rule = this._parse_rule(css_rule); + if (css_rule[0] instanceof Array) { + for (var i=0; i < css_rule.length; i++) + TestCaseUtil.Array.merge(founds, this.find_all(element, css_rule[i])); + } else { + var css_atom = css_rule.shift(); + switch (css_atom.rel) { + case '>': founds = this._find_immidiate_descendants(element, css_atom); break; + case '+': founds = this._find_next_sibling(element, css_atom); break; + case '~': founds = this._find_late_siblings(element, css_atom); break; + default: founds = this._find_descendants(element, css_atom); + } + if (css_rule.length > 0) { + var sub_founds = []; + for (var i=0; i < founds.length; i++) { + var css_rule_clone = []; + for (var j=0; j < css_rule.length; j++) + css_rule_clone.push(TestCaseUtil.extend({}, css_rule[j])); + TestCaseUtil.Array.merge(sub_founds, this.find_all(founds[i], css_rule_clone)); + } + founds = sub_founds; + } + } + return founds; + }, + _find_descendants: function(element, css_atom, immidiates_only) { + var founds = [], child = element.firstChild; + while (child) { + if (child.nodeType == 1) { + if (this._fully_match_atom(child, css_atom, element) && + !TestCaseUtil.Array.include(founds, child)) { + founds.push(child); + } + if (!immidiates_only) + TestCaseUtil.Array.merge(founds, this._find_descendants(child, css_atom)); + } + child = child.nextSibling; + } + return founds; + }, + _find_immidiate_descendants: function(element, css_atom) { + return this._find_descendants(element, css_atom, true); + }, + _find_next_sibling: function(element, css_atom) { + var founds = [], node = element.nextSibling; + while (node) { + if (node.nodeType == 1) { + if (this._fully_match_atom(node, css_atom, element) && + !TestCaseUtil.Array.include(founds, node)) + founds.push(node); + break; + } + node = node.nextSibling; + } + return founds; + }, + _find_late_siblings: function(element, css_atom) { + var founds = [], node = element.nextSibling; + while (node) { + if (node.nodeType == 1) { + if (this._fully_match_atom(node, css_atom, element) && + !TestCaseUtil.Array.include(founds, node)) + founds.push(node); + } + node = node.nextSibling; + } + return founds; + }, + _fully_match_atom: function(element, css_atom, parent_node) { + return this.match_atom(element, css_atom) && + this._match_pseudo(parent_node, element, css_atom.pseudo); + }, + _match_pseudo: function(element, child, pseudo) { + switch (pseudo) { + case 'first-child': return this._is_first_child(element, child); + case 'last-child': return this._is_last_child(element, child); + case 'only-child': return this._is_only_child(element, child); + default: return true; + } + }, + _is_first_child: function(element, child) { + var node = element.firstChild; + while (node) { + if (node.nodeType == 1) + return node == child; + node = node.nextSibling; + } + return false; + }, + _is_last_child: function(element, child) { + var node = element.lastChild; + while (node) { + if (node.nodeType == 1) + return node == child; + node = node.previousSibling; + } + return false; + }, + _is_only_child: function(element, child) { + var found = null, node = element.firstChild; + while (node) { + if (node.nodeType == 1) { + if (found) + return false; + else + found = node; + } + node = node.nextSibling; + } + return found == child; + }, + match_atom: function(element, css_atom) { + if (typeof css_atom == 'string') + css_atom = TestCaseUtil.Element.Selector._parse_atom(css_atom); + return (css_atom.tag_name == '*' || element.tagName == css_atom.tag_name) + && TestCaseUtil.Element.has_class_names(element, css_atom.class_names) + && TestCaseUtil.Element.Selector._match_css_attributes(element, css_atom.attrs) + && (!css_atom.id || element.id == css_atom.id); + }, + _match_css_attributes: function(element, attrs_def) { + for (var i=0; i < attrs_def.length; i++) { + var match = false; + switch (attrs_def[i].type) { + case '|=': + match = (typeof element[attrs_def[i].name] == 'string') && ( + element[attrs_def[i].name] == attrs_def[i].value || + new RegExp("(^|\-)" + attrs_def[i].value + "(\-|$)").test(element[attrs_def[i].name]) + ); + break; + case '~=': + match = (typeof element[attrs_def[i].name] == 'string') && ( + element[attrs_def[i].name] == attrs_def[i].value || + new RegExp("(^|\\s)" + attrs_def[i].value + "(\\s|$)").test(element[attrs_def[i].name]) + ); + break; + default: + match = element[attrs_def[i].name] == attrs_def[i].value; + } + if (!match) return false; + } + return true; + }, + _parse_rule: function(css_rule) { + var rules = css_rule.split(','); + var recognized_rules = []; + for (var i=0; i < rules.length; i++) { + var rule = rules[i].replace(/^\s+|\s+$/g, ''); + var match, separator_pos, recognized_rule = []; + var relation = null; + while ((match = rule.match(/(\s*([~>+ ])\s*)[^=]/))) { + separator_pos = rule.indexOf(match[0]); + recognized_rule.push({ + rule: rule.substring(0, separator_pos).replace(/^\s+|\s+$/g, ''), + rel: relation + }); + relation = match[2]; + rule = rule.substr(separator_pos+(match[1].length==1 ? 1 : match[1].length-1)).replace(/^\s+|\s+$/g, ''); + } + recognized_rule.push({ rule: rule, rel: relation }); + for (var j=0; j < recognized_rule.length; j++) + TestCaseUtil.extend(recognized_rule[j], this._parse_atom(recognized_rule[j].rule)); + recognized_rules.push(recognized_rule); + } + return recognized_rules; + }, + _id_re: /#[^\[\.]*/, + _attrs_re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/, + _parse_atom: function(rule) { + var atom = { + tag_name: '*', + id: null, + class_names: [], + pseudo: null, + attrs: [] + }; + var els = rule.split(':'); + if (els.length > 1) { + atom.pseudo = els[1].toLowerCase(); + rule = els[0]; + } + while ((attr_match = rule.match(this._attrs_re))) { + atom.attrs.push({ + name: attr_match[1], + type: attr_match[2], + value: attr_match[5] + }); + rule = rule.replace(attr_match[0], ''); + } + if ((id_match = rule.match(this._id_re))) { + atom.id = id_match[0].substr(1); + rule = rule.replace(id_match[0], ''); + } + atom.class_names = rule.split('.'); + atom.tag_name = atom.class_names.shift().toUpperCase(); + if (atom.tag_name == '') + atom.tag_name = '*'; + return atom; + } +}; + +/* ------------------------------------------------------------------- */ + +TestCaseUtil.Diff = { + calc: function(obj1, obj2) { + if (TestCaseUtil.get_object_class_name(obj1) != TestCaseUtil.get_object_class_name(obj2)) + throw "The given objects are instances of different classes"; + if (obj1 == null || obj2 == null) throw "One of the objects is null"; + if (obj1 == obj2) return []; + switch (typeof obj1) { + case 'string': return TestCaseUtil.Diff._string_diff.apply(TestCaseUtil.Diff, [obj1, obj2]); + case 'function': return TestCaseUtil.Diff._string_diff.apply(TestCaseUtil.Diff, [obj1.toString(), obj2.toString()]); + case 'boolean': + case 'number': return obj1 == obj2 ? [] : [{del: obj1}, {ins: obj2}]; + default: return TestCaseUtil.Diff[obj1 instanceof Array ? '_array_diff' : '_object_diff'].apply(TestCaseUtil.Diff, [obj1, obj2]); + } + }, + _string_diff: function(str1, str2) { + return this.__compact( + this.__calc_diff(str1.split("\n"), str2.split("\n")), + function (group, entry) { return group +"\n"+ entry; } + ); + }, + _array_diff: function(list1, list2) { + var diff = this.__calc_diff(list1, list2); + for (var i=0; i < diff.length; i++) { + var key = 'eql'; + if (typeof diff[i]['del'] != 'undefined') { + key = 'del'; + } else if (typeof diff[i]['ins'] != 'undefined') { + key = 'ins'; + } + } + return diff; + }, + _object_diff: function(obj1, obj2) { + var lists = [[], []], objects = [obj1, obj2]; + for (var i=0; i < 2; i++) { + for (var key in objects[i]) { + lists[i].push( key+"{:_key-sep_:}"+TestCaseUtil.to_s(objects[i][key]) ); + } + } + var diff = this.__calc_diff(lists[0], lists[1]); + var collect = { eql: {}, ins: {}, del: {} }; + for (var i=0; i < diff.length; i++) { + var key = 'eql'; + if (diff[i]['ins']) { + key = 'ins'; + } else if (diff[i]['del']) { + key = 'del'; + } + var a_key = diff[i][key].split("{:_key-sep_:}")[0]; + collect[key][a_key] = (diff[i]['ins'] ? obj2 : obj1)[a_key]; + } + var diff = [], keys = ['del', 'ins', 'eql']; + for (var i=0; i < keys.length; i++) { + if (!TestCaseUtil.equal(collect[keys[i]], {})) { + var el = {}; el[keys[i]] = collect[keys[i]]; + diff.push(el); + } + } + return diff; + }, + __calc_diff: function(first, second) { + var diff = [], start_k = 0; + for (var i=0; i < first.length; i++) { + var eql_found = false; + var inses = []; + for (var k=start_k; k < second.length; k++) { + if (TestCaseUtil.equal(first[i], second[k])) { + eql_found = second[k]; + start_k = k+1; + break; + } else { + inses.push(second[k]); + } + } + if (eql_found) { + for (var j=0; j < inses.length; j++) { + diff.push({ins: inses[j]}); + } + diff.push({eql: eql_found}); + } else { + diff.push({del: first[i]}); + } + } + for (var k=start_k; k < second.length; k++) { + diff.push({ins: second[k]}); + } + var only_eqls = true; + for (var i=0; i < diff.length; i++) { + if (diff[i]['del'] || diff[i]['ins']) { + only_eqls = false; + break; + } + } + return only_eqls ? [] : diff; + }, + __compact: function(diff, compactor) { + if (diff.length == 0) { return diff; } + var c_diff = []; + var last_o = diff[0]; + for (var i=1; i < diff.length; i++) { + var key = 'eql'; + if (typeof diff[i]['ins'] != 'undefined') { + key = 'ins'; + } else if (typeof diff[i]['del'] != 'undefined') { + key = 'del'; + } + if (typeof last_o[key] != 'undefined') { + last_o[key] = compactor(last_o[key], diff[i][key]); + } else { + c_diff.push(last_o); + last_o = diff[i]; + } + } + c_diff.push(last_o); + return c_diff; + } +}; +/* ------------------------------------------------------------------- */ + +TestCaseUtil.Cookie = { + enabled: function() { + document.cookie = "test_cookie=test"; + return document.cookie.indexOf("test_cookie=test")!=-1; + }, + set: function(key, value, keep_days) { + if (keep_days) { + var date = new Date();date.setTime(date.getTime()+(100*24*60*60*1000)); + } + document.cookie = key+"="+escape(value)+(keep_days ? "; expires="+date.toGMTString() : ""); + }, + get: function(key) { + var value = '', pairs = document.cookie.split(";"); + for (var i=0; i < pairs.length; i++) { + var pair = pairs[i].split('='); + if (pair.length == 2 && pair[0].replace(/^\s+|\s+$/g, '') == key) + value = pair[1].replace(/^\s+|\s+$/g, ''); + } + return unescape(value); + } +}; +/* ------------------------------------------------------------------- */ + +var TestCase = TestCaseUtil.create_class(); +TestCaseUtil.extend(TestCase, { + version: "2.0.2", + create: function(defs) { + var Test = TestCaseUtil.create_class(); + TestCaseUtil.extend(Test, TestCase); + Test.prototype = new TestCase(); + Test.extend = function(entries) { + TestCaseUtil.extend(this.prototype, entries); + this.wire_custom_assertions(entries); + }; + Test.extend(defs || {}); + return Test; + } +}); +TestCaseUtil.extend(TestCase.prototype, { + name: null, + title: null, + size: 0, + failed: 0, + passed: 0, + assertions: 0, + initialize: function(title) { + this.title = (this.name || "Unnamed Test") + (title ? ": "+title : ""); + this.size = 0; + this.passed = 0; + this.assertations = 0; + for (key in this) { + if (key.substring(0, 4) == "test") { + this.size ++; + } + } + } +}); + +/* ------------------------------------------------------------------- */ + +TestCase.Starter = { + run: function(title) { + var test = this._get_test_instance(); + this._register_test(test); + test.passed = 0; + test.failed = 0; + test.assertions = 0; + if (this.reporter && this.reporter.ui_builder && + this.reporter.ui_builder.test_is_skipped(test)) { + test.skipped = true; + this.reporter.ui_builder.create_summary_report(this.reporter.current_test_block, test); + return test; + } + this._call_before_all(test); + try { + var test_names = this._get_test_methods(test); + for (var i=0; i < test_names.length; i++) { + var name = test_names[i]; + this._report_test_started(name); + this._call_setup(test); + test.manual_failure = null; + var test_start_assertion_num = test.assertions; + try { + test[name](); + if (test.manual_failure) + throw new TestCase.Exception(test.manual_failure); + test.passed++; + this._report_test_passed(name); + } catch (e) { + if (e instanceof TestCase.Exception) { + test.failed++; + this._report_failed(name, e, test.assertions - test_start_assertion_num + 1); + } else { + throw e; + } + } finally { + this._call_teardown(test); + } + } + } finally { + this._call_after_all(test); + } + return test; + }, + run_on_load: function() { + var $this = this; + TestCaseUtil.Event.observe(window, 'load', function() { + $this.run(); + }); + }, + _get_test_instance: function(title) { + if (typeof this == 'object') { + var test = this; + } else { + var Test = this; + var test = new Test(title); + } + return test; + }, + _get_test_methods: function(test) { + var test_method_names = []; + for (key in test) + if (key.substring(0, 4) == "test" && typeof test[key] == 'function') + test_method_names.push(key); + if (navigator.userAgent.indexOf("MSIE") != -1) { test_method_names.reverse(true); } + return test_method_names; + }, + _call_setup: function(test) { + if (test['setup']) test.setup.apply(test); + else if (test['setUp']) test.setUp.apply(test); + }, + _call_teardown: function(test) { + if (test['teardown']) test.teardown.apply(test); + else if (test['tearDown']) test.tearDown.apply(test); + }, + _call_before_all: function(test) { + if (test['before_all']) test.before_all.apply(test); + else if (test['beforeAll']) test.beforeAll.apply(test); + }, + _call_after_all: function(test) { + if (test['after_all']) test.after_all.apply(test); + else if (test['afterAll']) test.afterAll.apply(test); + }, + _register_test: function(test) { + if (this.reporter) { this.reporter.register(test); } + }, + _report_test_started: function(test_name) { + if (this.reporter) { this.reporter.test_started(test_name); } + }, + _report_test_passed: function(test_name) { + if (this.reporter) { this.reporter.test_passed(test_name); } + }, + _report_failed: function(test_name, error, assertion_num) { + if (this.reporter) { + this.reporter.test_failed(test_name, error, assertion_num); + } else { + throw error; + } + } +}; +/* ------------------------------------------------------------------- */ + +TestCase.Mocking = { + mock: function(object, name, mock) { + if (!object[name]) throw "The object have no method '"+name+"' to mock it up"; + var o_mocks = TestCase.Mocking._get_mocks_of(object); + o_mocks[name] = o_mocks[name] || object[name]; + return object[name] = mock; + }, + undo_mock: function(object, name) { + if (this._has_mock_for(object, name)) { + var o_mocks = TestCase.Mocking._get_mocks_of(object); + if (o_mocks[name]) { + object[name] = o_mocks[name]; + o_mocks[name] = null; + } + } + return object[name]; + }, + with_mock: function(object, name, mock, func, scope) { + this.mock(object, name, mock); + try { + func.apply(scope); + } finally { + this.undo_mock(object, name); + } + }, + mock_effects: function() { + this._run_if_mockable('mock_effects'); + }, + undo_effects_mock: function() { + this._run_if_mockable('undo_effects_mock'); + }, + with_effects_mock: function(func, scope) { + this.mock_effects(); + try { + func.apply(scope); + } finally { + this.undo_effects_mock(); + } + }, + mock_ajax: function(options) { + this._run_if_mockable('mock_ajax', options); + }, + undo_ajax_mock: function() { + this._run_if_mockable('undo_ajax_mock'); + }, + with_ajax_mock: function(options, func, scope) { + this.mock_ajax(); + try { + func.apply(scope); + } finally { + this.undo_ajax_mock(); + } + }, + __mock_ups: [], + _get_mocks_of: function(object) { + var o_mocks = null; + var mockups = TestCase.Mocking.__mock_ups; + for (var i=0; i < mockups.length; i++) { + if (mockups[i]['object'] == object) { + o_mocks = mockups[i]; + break; + } + } + if (!o_mocks) { + o_mocks = { + 'object': object, 'mocks': {} + }; + TestCase.Mocking.__mock_ups.push(o_mocks); + } + return o_mocks['mocks']; + }, + _has_mock_for: function(object, name) { + var mockups = TestCase.Mocking.__mock_ups; + for (var i=0; i < mockups.length; i++) { + if (mockups[i]['object'] == object) { + return typeof mockups[i]['mocks'][name] != 'undefined' && mockups[i]['mocks'][name] != null; + } + } + return false; + }, + _mockable_libs: ['Prototype', 'MooTools', 'jQuery'], + _run_if_mockable: function() { + var args = TestCaseUtil.to_a(arguments); + var method_name = args.shift(); + if (method_name == 'mock_ajax') { + this._raw_mockup_ajax.apply(this, args); + } else if (method_name == 'undo_ajax_mock') { + this._raw_undo_ajax_mockup(); + } + for (var i=0; i < this._mockable_libs.length; i++) { + if (self[this._mockable_libs[i]]) { + return this[this._mockable_libs[i]][method_name].apply(this[this._mockable_libs[i]], args); + } + } + }, + _raw_mockup_ajax: function(params) { + if (TestCaseUtil.Browser.IE) { + this.__ms_raw_ajax_mock(params); + } else { + this.__w3c_raw_ajax_mock(params); + } + }, + __w3c_raw_ajax_mock: function(params) { + if (!(new XMLHttpRequest instanceof TestCase.Mocking.FakeXMLHttpRequest)) { + this.mock(self, 'XMLHttpRequest', function() { + return new TestCase.Mocking.FakeXMLHttpRequest(params); + }); + } + }, + __ms_raw_ajax_mock: function(params) { + if (!(new ActiveXObject('Msxml2.XMLHTTP') instanceof TestCase.Mocking.FakeXMLHttpRequest)) { + var _ = ActiveXObject; + this.mock(self, 'ActiveXObject', function(param) { + if (param.indexOf('XMLHTTP') != -1) { + return new TestCase.Mocking.FakeXMLHttpRequest(params); + } else { + return new _(param); + } + }); + } + }, + _raw_undo_ajax_mockup: function() { + if (TestCaseUtil.Browser.IE) { + this.undo_mock(self, 'ActiveXObject'); + } else { + this.undo_mock(self, 'XMLHttpRequest'); + } + } +}; + +/* ------------------------------------------------------------------- */ + +TestCase.Mocking.FakeXMLHttpRequest = TestCaseUtil.create_class({ + onreadystatechange: null, + readyState: null, + status: null, + statusText: null, + responseText: null, + responseXML: null, + fakeData: null, + initialize: function(options) { + this.fakeData = TestCaseUtil.extend({ + status: 200, + xml: null, + text: '', + headers: {} + }, options || {}); + this.onreadystatechange = function() {}; + this.readyState = 0; + }, + __onSend: function() { + this.readyState = 2; + this.status = this.fakeData.status; + this.onreadystatechange(); + this.readyState = 3; + this.responseText = ''; + this.onreadystatechange(); + this.readyState = 4; + this.responseText = this.fakeData.text; + this.responseXML = this.fakeData.xml; + this.onreadystatechange(); + }, + open: function(method, url, async, user, password) { + this.readyState = 1; + }, + send: function(body) { + this.__onSend(); + }, + setRequestHeader: function(name, value) { + }, + getResponseHeader: function(name) { + for (var key in this.fakeData.headers) { + if (key.toLowerCase() == name.toLowerCase()) + return this.fakeData.headers[key]; + } + return null; + }, + getAllResponseHeaders: function() { + var headers = []; + for (var key in this.fakeData.headers) { + headers.push(key+": "+this.fakeData.headers[key]); + } + return headers.join("\n\n"); + }, + overrideMimeType: function(mimetype) {}, + addEventListener: function(type, listener, useCapture) {}, + removeEventListener: function(type, listener, useCapture) {}, + dispatchEvent: function(event) {}, + dispatchException: function(e) { + throw e; + }, + abort: function() {} +}); +/* ------------------------------------------------------------------- */ + +TestCase.Mocking.Prototype = { + hide_effects: ['Fade', 'SlideUp', 'BlindUp', 'SwitchOff'], + show_effects: ['Appear', 'SlideDown', 'BlindDown', 'Grow'], + mock_effects: function() { + if (!self['Effect']) return false; + for (var i=0; i < this.hide_effects.length; i++) { + TestCase.mock(Effect, this.hide_effects[i], function(element, options) { + Element.hide(element); + if (options.afterFinish) options.afterFinish(); + return element; + }); + TestCase.mock(Effect, this.show_effects[i], function(element, options) { + Element.show(element); + if (options.afterFinish) options.afterFinish(); + return element; + }); + } + }, + undo_effects_mock: function() { + if (!self['Effect']) return false; + for (var i=0; i < this.hide_effects.length; i++) { + TestCase.undo_mock(Effect, this.hide_effects[i]); + TestCase.undo_mock(Effect, this.show_effects[i]); + } + }, + mock_ajax: function(options) { + if (!self['Ajax']) { return false; } + TestCase.mock(Ajax, 'getTransport', function() { + return new TestCase.Mocking.FakeXMLHttpRequest(options); + }); + }, + undo_ajax_mock: function() { + if (!self['Ajax']) { return false; } + TestCase.undo_mock(Ajax, 'getTransport'); + } +}; +/* ------------------------------------------------------------------- */ + +TestCase.Mocking.MooTools = { + mock_effects: function() { + if (!self['Fx']) { return false; } + if (Fx['Tween']) { + TestCase.mock(Fx.Tween.prototype, 'start', function(property, from, to) { + this.onStart(); + this.set(property, from); + if (to) { + this.set(property, to); + } + this.onComplete(); + }); + } + if (Fx['Morph']) { + TestCase.mock(Fx.Morph.prototype, 'start', function(properties) { + this.onStart(); + var from = {}, to = {}; + for (var key in properties) { + if (properties[key] instanceof Array) { + from[key] = properties[key][0]; + to[key] = properties[key][1]; + } else { + from[key] = properties[key]; + } + } + this.set(from); + if (!TestCaseUtil.equal(to, {})) { + this.set(to); + } + this.onComplete(); + }); + } + }, + undo_effects_mock: function() { + if (!self['Fx']) { return false; } + if (Fx['Tween']) TestCase.undo_mock(Fx.Tween.prototype, 'start'); + if (Fx['Morph']) TestCase.undo_mock(Fx.Morph.prototype, 'start'); + }, + mock_ajax: function(options) { + if (!self['Request']) { return false; } + var old_initialize = Request.prototype.initialize; + TestCase.mock(Request.prototype, 'initialize', function() { + old_initialize.apply(this, arguments); + this.xhr = new TestCase.Mocking.FakeXMLHttpRequest(options); + }); + }, + undo_ajax_mock: function() { + if (!self['Request']) { return false; } + TestCase.undo_mock(Request.prototype, 'initialize'); + } +}; +/* ------------------------------------------------------------------- */ + +TestCase.Mocking.jQuery = { + mock_effects: function() { + }, + undo_effects_mock: function() { + }, + mock_ajax: function(options) { + }, + undo_ajax_mock: function() { + } +}; +/* ------------------------------------------------------------------- */ + +TestCase.Fires = { + FIRES_DEFAULT_MOUSE_EVENT_OPTIONS: { + pointerX: 0, + pointerY: 0, + button: 0, + bubbles: true, + cancelable: true, + altKey: false, + ctrlKey: false, + shiftKey: false, + metaKey: false + }, + FIRES_DEFAULT_KEYBOARD_EVENT_OPTIONS: { + keyCode: 0, + charCode: 0, + bubbles: true, + cancelable: true, + altKey: false, + ctrlKey: false, + shiftKey: false, + metaKey: false + }, + fire_mouse_event: function(element, eventName, options) { + return this._fire_event(this._$(element), + this._create_mouse_event(this._$(element), eventName, options) + ); + }, + fire_key_event: function(element, eventName, keyCode, options) { + return this._fire_event(this._$(element), + this._create_key_event(this._$(element), eventName, + TestCaseUtil.extend((options || {}), {keyCode: keyCode, charCode: keyCode}) + ) + ); + }, + fire_click: function(element, options) { + return this.fire_mouse_event(element, 'click', options); + }, + fire_middle_click: function(element, options) { + return this.fire_mouse_event(element, 'middleclick', TestCaseUtil.extend({button: 1}, options || {})); + }, + fire_right_click: function(element, options) { + return this.fire_mouse_event(element, 'rightclick', TestCaseUtil.extend({button: 2}, options || {})); + }, + fire_double_click: function(element, options) { + return this.fire_mouse_event(element, 'dblclick', options); + }, + fire_mouse_down: function(element, options) { + return this.fire_mouse_event(element, 'mousedown', options); + }, + fire_mouse_up: function(element, options) { + return this.fire_mouse_event(element, 'mouseup', options); + }, + fire_mouse_over: function(element, options) { + return this.fire_mouse_event(element, 'mouseover', options); + }, + fire_mouse_out: function(element, options) { + return this.fire_mouse_event(element, 'mouseout', options); + }, + fire_mouse_move: function(element, options) { + return this.fire_mouse_event(element, 'mousemove', options); + }, + fire_key_press: function(element, key, options) { + return this.fire_key_event(element, 'keypress', key, options); + }, + fire_key_down: function(element, key, options) { + return this.fire_key_event(element, 'keydown', key, options); + }, + fire_key_up: function(element, key, options) { + return this.fire_key_event(element, 'keyup', key, options); + }, + _$: function(element) { + element = TestCaseUtil.$(element); + if (element == document && document.createEvent && !element.dispatchEvent) + element = document.documentElement; + return element; + }, + _fire_event: function(element, event) { + if (document.createEvent) { + element.dispatchEvent(event); + } else { + if (TestCaseUtil.Element.up(element, 'body')) { + if (event.eventType != 'onmiddleclick') + element.fireEvent(event.eventType, event); + } else { + this.fail("Please put your element on the page to fire an event on it in IE"); + } + } + return event; + }, + _create_mouse_event: function(element, eventName, options) { + return (document.createEvent ? this._create_w3c_mouse_event : this._create_ie_mouse_event).apply( + this, [element, eventName, TestCaseUtil.extend(this.FIRES_DEFAULT_MOUSE_EVENT_OPTIONS, options || {})]); + }, + _create_w3c_mouse_event: function(element, eventName, options) { + var event = document.createEvent("MouseEvents"); + event.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView, + eventName == 'dblclick' ? 2 : 1, options.pointerX, options.pointerY, options.pointerX, options.pointerY, + options.ctrlKey, options.altKey, options.shiftKey, options.metakey, options.button, element + ); + event.eventName = eventName; + return event; + }, + _create_ie_mouse_event: function(element, eventName, options) { + var event = this._create_ie_event(eventName, options); + event.clientX = options.pointerX; + event.clientY = options.pointerY; + event.button = options.button == 1 ? 4 : options.button; + return event; + }, + _create_key_event: function(element, eventName, options) { + return (document.createEvent ? this._create_w3c_keyboard_event : this._create_ie_keyboard_event).apply( + this, [eventName, TestCaseUtil.extend(this.FIRES_DEFAULT_KEYBOARD_EVENT_OPTIONS, options || {})]); + }, + _create_w3c_keyboard_event: function(eventName, options) { + return (TestCaseUtil.Browser.Gecko ? this._create_gecko_keyboard_event : ( + TestCaseUtil.Browser.WebKit ? this._create_webkit_keyboard_event : this._create_dom2_ui_event + )).apply(this, [eventName, options]); + }, + _create_gecko_keyboard_event: function(eventName, options) { + var event = document.createEvent("KeyboardEvent"); + event.initKeyEvent(eventName, + options.bubbles, options.cancelable, document.defaultView, + options.ctrlKey, options.altKey, options.shiftKey, options.metakey, + options.keyCode, options.charCode + ); + event.eventName = eventName; + return event; + }, + _create_webkit_keyboard_event: function(eventName, options) { + var event = document.createEvent("KeyboardEvent"); + event.initKeyboardEvent(eventName, + options.bubbles, options.cancelable, document.defaultView, + null, 0, + options.ctrlKey, options.altKey, options.shiftKey, options.metakey + ); + event.eventName = eventName; + return event; + }, + _create_dom2_ui_event: function(eventName, options) { + var event = document.createEvent("UIEvent"); + event.initUIEvent(eventName, options.bubbles, options.cancelable, document.defaultView, 1); + event.keyCode = options.keyCode; + event.charCode = options.charCode; + event.altKey = options.altKey; + event.metaKey = options.metaKey; + event.ctrlKey = options.ctrlKey; + event.shiftKey = options.shiftKey; + event.eventName = eventName; + return event; + }, + _create_ie_keyboard_event: function(eventName, options) { + var event = this._create_ie_event(eventName, options); + event.keyCode = options.keyCode; + return event; + }, + _create_ie_event: function(eventName, options) { + var event = document.createEventObject(); + event.eventName = eventName == 'rightclick' ? 'contextmenu' : eventName; + event.type = event.eventType = "on" + event.eventName; + event.altKey = options.altKey; + event.ctrlKey = options.ctrlKey; + event.shiftKey = options.shiftKey; + return event; + } +}; +/* ------------------------------------------------------------------- */ + +TestCase.Exception = TestCaseUtil.create_class(Error, { + test: null, + assertion: null, + message: null, + initialize: function(test, assertion, message) { + this.test = test; + this.assertion = assertion; + this.message = message; + }, + toString: function() { + return "TestCase.Exception: "+this.message; + } +}); +TestCase.ProblemException = TestCaseUtil.create_class(TestCase.Exception, { + problem: null, + initialize: function(test, assertion, problem, message) { + this.test = test; + this.assertion = assertion; + this.problem = problem; + this.message = message; + }, + toString: function() { + return "TestCase.ProblemException: "+this.problem+(this.message ? " ("+this.message+")" : ""); + } +}); +TestCase.WrongValueException = TestCaseUtil.create_class(TestCase.Exception, { + expected: null, + received: null, + format: false, + initialize: function(test, assertion, expected, received, message, format) { + this.test = test; + this.assertion = assertion; + this.expected = expected; + this.received = received; + this.message = message; + this.format = format; + }, + toString: function() { + return "TestCase.WrongValueException: "+this.expected+" -> "+this.received; + } +}); +/* ------------------------------------------------------------------- */ + +TestCase.Messaging = { + fail: function(message) { + this.manual_failure = message; + throw new TestCase.Exception(this, this._last_assertion(), message); + }, + debug: function(variable) { + alert(TestCaseUtil.debug(variable)); + }, + throw_problem: function(error, message) { + throw new TestCase.ProblemException(this, this._last_assertion(), error, message); + }, + throw_unexp: function(expected, received, message, format) { + throw new TestCase.WrongValueException(this, this._last_assertion(), expected, received, message, format); + }, + _last_assertion: function() { + return this['assert_'+this.last_assert_name]; + } +}; + +/* ------------------------------------------------------------------- */ + +TestCase.Assertions = { + assert_true: function(value, message) { + if (!value) + this.throw_unexp(true, value, message); + }, + assert_false: function(value, message) { + if (value) + this.throw_unexp(false, value, message); + }, + assert_null: function(value, message) { + if (value != null) + this.throw_unexp(null, value, message); + }, + assert_not_null: function(value, message) { + if (value == null) + this.throw_problem("The value is null", message); + }, + assert_type_of: function(type, value, message) { + if (typeof(value) != type) + this.throw_unexp(type.toLowerCase(), typeof(value), message); + }, + assert_instance_of: function(type, value, message) { + if (!(value instanceof type)) + this.throw_unexp(TestCaseUtil.get_class_name(type), TestCaseUtil.get_object_class_name(value), message); + }, + assert_equal: function(expected, testing, message) { + if (!TestCaseUtil.equal(expected, testing)) + this.throw_unexp(expected, testing, message, true); + }, + assert_not_equal: function(expected, testing, message) { + if (TestCaseUtil.equal(expected, testing)) + this.throw_problem("Received values are equal", message); + }, + assert_same: function(expected, testing, message) { + if (expected != testing) + this.throw_problem("Values do not point to the same object", message); + }, + assert_not_same: function(expected, testing, message) { + if (expected == testing) + this.throw_problem("Values point to the same object", message); + }, + assert_match: function(pattern, string, message) { + if( !string.match(pattern) ) + this.throw_problem("'"+string+"' doesn't match '"+pattern+"'", message); + }, + assert_not_match: function(pattern, string, message) { + if( string.match(pattern) ) + this.throw_problem("'"+string+"' match '"+pattern+"'", message); + }, + assert_throws: function() { + var params = this._assert_throw_params.apply(this, arguments); + var catched = true; + try { params.callback.apply(params.scope_element); + catched = false; + } catch (e) { catched = e; } + if (!catched) + this.throw_problem('No exception raised during the call', params.message); + else if (params.exception_type) { + if (!this._type_of(catched, params.exception_type)) + this.throw_unexp("Exception with type of: "+params.exception_type, catched, params.message); + } + }, + assert_nothing_thrown: function() { + var params = this._assert_throw_params.apply(this, arguments); + try { params.callback.apply(params.scope_element); + } catch (e) { + if (params.exception_type) { + if (this._type_of(e, params.exception_type)) + this.throw_problem("Unexpected exception with type '"+params.exception_type+ + "' has been raised: \""+e.toString()+"\"", params.message); + } else { + this.throw_problem('Unexpected exception has been raised: "'+e.toString()+'"', params.message); + } + } + }, + _assert_throw_params: function() { + var params = typeof arguments[1] == 'function' ? + { exception_type: arguments[0], callback: arguments[1]} : + { callback: arguments[0]}; + params.message = null; + for (var i=0; i < arguments.length; i++) + if (arguments[i].scope) + params.scope_element = arguments[i].scope; + if (arguments.length > 1) { + for (var i=arguments.length-1; i > 0; i--) { + if (arguments[i] == params.callback) { + break; + } else if (typeof arguments[i] == 'string') { + params.message = arguments[i]; + } else if (typeof params.scope_element == 'undefined') { + params.scope_element = arguments[i]; + } + } + } + return params; + }, + _type_of: function(object, _type) { + switch(_type) { + case 'number': + case Number: return typeof object == 'number'; + case 'string': + case String: return typeof object == 'string'; + default: return object instanceof _type; + } + }, + assert_called: function() { + var problems = []; + this._execute_wrapped_call(arguments, problems, + function(flag, call) { + if (!flag) problems.push("Attribute '"+call[1]+"' was not called"); + } + ); + }, + assert_not_called: function() { + var problems = []; + this._execute_wrapped_call( arguments, problems, + function(flag, call) { + if (flag) problems.push("Attribute '"+call[1]+"' was unexpetedly called"); + } + ); + }, + _execute_wrapped_call: function(args, problems, callback) { + var params = this._assert_called_params.apply(this, args); + try { + var flags = []; + for (var i=0; i < params.calls.length; i++) { + flags[i] = false; + this.mock(params.calls[i][0], params.calls[i][1], (function(flags, i, object, name) { + var method = object[name]; + return function() { + flags[i] = true; + return method.apply(object, arguments); + }; + })(flags, i, params.calls[i][0], params.calls[i][1])); + } + params.callback.apply(params['scope_element']); + for (var i=0; i < flags.length; i++) { + if (params.calls[i]) + callback.apply(this, [flags[i], params.calls[i]]); + } + } finally { + for (var i=0; i < params.calls.length; i++) { + this.undo_mock(params.calls[i][0], params.calls[i][1]); + } + } + if (problems.length) { + this.throw_problem(problems.join("\n"), params.message); + } + }, + _assert_called_params: function() { + var params = { calls: [], callback: null, message: null }; + if (typeof arguments[1] == 'string') { + params.calls = [[arguments[0], arguments[1]]]; + params.callback = arguments[2]; + } else if (arguments[0] instanceof Array) { + if (arguments[0].length == 2 && typeof(arguments[0][1]) == 'string') { + params.calls = [arguments[0]]; + } else { + params.calls = arguments[0]; + } + params.callback = arguments[1]; + } + for (var i=0; i < arguments.length; i++) + if (arguments[i].scope) + params.scope_element = arguments[i].scope; + if (arguments.length > 2) { + for (var i=arguments.length-1; i > 0; i--) { + if (arguments[i] == params.callback) { + break; + } else if (typeof arguments[i] == 'string') { + params.message = arguments[i]; + } else if (typeof params.scope_element == 'undefined') { + params.scope_element = arguments[i]; + } + } + } + return params; + }, + assert_exists: function(css_rule, message) { + if (TestCaseUtil.$$(css_rule).length == 0) + this.throw_problem("Cannot find any element like '"+css_rule+"'", message); + }, + assert_not_exists: function(css_rule, message) { + if (TestCaseUtil.$$(css_rule).length) + this.throw_problem("Element '"+css_rule+"' presents on the page", message); + }, + assert_has_child: function(element, css_rule, message) { + if (!TestCaseUtil.Element.down(element, css_rule)) + this.throw_problem("Element has no child element like '"+css_rule+"'", message); + }, + assert_has_no_child: function(element, css_rule, message) { + if (TestCaseUtil.Element.down(element, css_rule)) + this.throw_problem("Element has child element '"+css_rule+"'", message); + }, + assert_has_parent: function(element, css_rule, message) { + if (!TestCaseUtil.Element.up(element, css_rule)) + this.throw_problem("Element has no parent element like '"+css_rule+"'", message); + }, + assert_has_no_parent: function(element, css_rule, message) { + if (TestCaseUtil.Element.up(element, css_rule)) + this.throw_problem("Element has parent element '"+css_rule+"'", message); + }, + assert_visible: function(element_or_css_rule, message) { + var elements = typeof element_or_css_rule == 'string' ? TestCaseUtil.$$(element_or_css_rule) : [element_or_css_rule]; + if (!elements.length || !elements[0]) + this.throw_unexp("Element is visible", "Element is not found", message); + else { + for (var i=0; i < elements.length; i++) { + var element_style = this.__get_element_style(elements[i]); + var element_display = elements[i].style.display || element_style['display']; + var element_visibility = elements[i].style.visibility || element_style['visibility']; + if (element_display == 'none' || element_visibility == 'hidden') + this.throw_problem("Element is not visible", message); + } + } + }, + assert_hidden: function(element_or_css_rule, message) { + var elements = typeof element_or_css_rule == 'string' ? TestCaseUtil.$$(element_or_css_rule) : [element_or_css_rule]; + if (!elements.length || !elements[0]) + this.throw_unexp("Element is hidden", "Element is not found", message); + else { + for (var i=0; i < elements.length; i++) { + var element_style = this.__get_element_style(elements[i]); + var element_display = elements[i].style.display || element_style['display']; + var element_visibility = elements[i].style.visibility || element_style['visibility']; + if (element_display != 'none' && element_visibility != 'hidden') + this.throw_problem("Element is visible", message); + } + } + }, + assert_has_class_name: function(element, class_name, message) { + if (!TestCaseUtil.Element.has_class_name(element, class_name)) + this.throw_problem("Element has no class-name '"+class_name+"'", message); + }, + assert_has_no_class_name: function(element, class_name, message) { + if (TestCaseUtil.Element.has_class_name(element, class_name)) + this.throw_problem("Element have class-name '"+class_name+"'", message); + }, + assert_has_attribute: function(element, name, message) { + if (element.getAttribute(name) == null) + this.throw_problem("Element has no attribute '"+name+"'", message); + }, + assert_has_no_attribute: function(element, name, message) { + if (element.getAttribute(name) != null) + this.throw_problem("Element has attribute '"+name+"'", message); + }, + assert_style: function(element, style, message) { + var element_computed_style = this.__get_element_style(element); + var element_style = {}; + for (var key in style) { + if (key.toLowerCase().substring(key.length-5) == 'color') { + var t = document.createElement('span'); + t.style[key] = style[key]; + style[key] = t.style[key]; + } + if (element.style && element.style[key]) { + element_style[key] = element.style[key]; + } else if (element_computed_style[key]) { + element_style[key] = element_computed_style[key]; + } else { + element_style[key] = null; + } + }; + if (!TestCaseUtil.equal(style, element_style)) + this.throw_unexp("Element has style:\n "+TestCaseUtil.to_s(style), + TestCaseUtil.to_s(element_style), message); + }, + __get_element_style: function(element) { + var style = document.defaultView ? document.defaultView.getComputedStyle(element, null) : element.currentStyle; + return style ? style : {}; + } +}; +/* ------------------------------------------------------------------- */ + +TestCase.AssertionsExtender = { + add_assertion: function(name, func) { + var name = name.substr(0, 7) == 'assert_' ? name.substr(7, name.length) : name; + var assert = {}; assert['assert_'+name] = function() { + this._count_assert(name); + func.apply(this, arguments); + }; + TestCaseUtil.extend(this.prototype ? this.prototype : this, assert); + TestCaseUtil.extend_with_camelized_aliases(this.prototype ? this.prototype : this, assert); + }, + add_assertions: function(module) { + TestCaseUtil.extend(this.prototype ? this.prototype : this, module); + TestCaseUtil.extend_with_camelized_aliases(this.prototype ? this.prototype : this, module); + for (var key in module) { + if (key.substr(0, 7) == 'assert_') + this.add_assertion(key, module[key]); + } + }, + wire_custom_assertions: function(test_def) { + for (var name in test_def) { + if (name.substr(0, 6) == 'assert' && typeof(test_def[name]) == 'function') { + this.add_assertion(name.replace(/([a-z]+)([A-Z])/g, '$1_$2').toLowerCase(), this.prototype[name]); + } + } + }, + _count_assert: function(name) { + this.assertions++; + this.last_assert_name = name; + } +}; +/* ------------------------------------------------------------------- */ + +TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.Starter); +TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.Fires); +TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.Mocking); +var __mock_aliaces = { + mockUp: TestCase.mock, + mockup: TestCase.mock, + undo_mockup: TestCase.undo_mock, + undoMockup: TestCase.undo_mock, + undoMockUp: TestCase.undo_mock, + with_mocked: TestCase.with_mock, + withMocked: TestCase.with_mock, + mockupEffects: TestCase.mock_effects, + mockUpEffects: TestCase.mock_effects, + mockup_effects: TestCase.mock_effects, + undo_effects_mockup: TestCase.undo_effects_mock, + undoEffectsMockup: TestCase.undo_effects_mock, + undoMockUpEffects: TestCase.undo_effects_mock, + undoMockupEffects: TestCase.undo_effects_mock, + undo_mockup_effects: TestCase.undo_effects_mock, + mockUpAjax: TestCase.mock_ajax, + mockupAjax: TestCase.mock_ajax, + mockup_ajax: TestCase.mock_ajax, + undoMockupAjax: TestCase.undo_ajax_mock, + undoMockUpAjax: TestCase.undo_ajax_mock, + undo_mockup_ajax: TestCase.undo_ajax_mock, + undo_ajax_mockup: TestCase.undo_ajax_mock, + undoAjaxMockup: TestCase.undo_ajax_mock, + undoAjaxMockUp: TestCase.undo_ajax_mock, + with_ajax_mockup: TestCase.with_ajax_mock +}; +TestCaseUtil.extend(TestCase, __mock_aliaces); +TestCaseUtil.extend(TestCase.prototype, __mock_aliaces); +TestCaseUtil.extend(TestCase.prototype, TestCase.Messaging); +TestCaseUtil.extend_fully_object_and_prototype(TestCase, TestCase.AssertionsExtender); +TestCase.add_assertions(TestCase.Assertions); +TestCaseUtil.extend(TestCase.prototype, { + flunk: TestCase.prototype.fail, + assert: TestCase.prototype.assert_true, + assert_nil: TestCase.prototype.assert_null, + assert_not_nil: TestCase.prototype.assert_not_null, + assert_kind_of: TestCase.prototype.assert_type_of, + assert_no_match: TestCase.prototype.assert_not_match, + assertEquals: TestCase.prototype.assert_equal, + assertNotEquals: TestCase.prototype.assert_not_equal, + assertNoMatch: TestCase.prototype.assert_not_match +}); +TestCase.prototype.util = TestCaseUtil; + +/* ------------------------------------------------------------------- */ + +var TestSuite = TestCaseUtil.create_class({ + title: null, + reporter: null, + test_cases: [], + initialize: function() { + this.test_cases = []; + this.add.apply(this, arguments); + }, + add: function() { + for (var i=0; arguments.length > i; i++) { + this.test_cases.push(arguments[i]); + } + this.test_cases = TestCaseUtil.Array.uniq(this.test_cases); + return this; + }, + remove: function() { + for (var i=0; i < arguments.length; i++) + this.test_cases = TestCaseUtil.Array.without(this.test_cases, arguments[i]); + return this; + }, + run: function(title) { + for (var i=0; i < this.test_cases.length; i++) + this.test_cases[i].run(); + return this; + }, + run_on_load: function() { + var $this = this; + TestCaseUtil.Event.observe(window, 'load', function() { + $this.run(); + }); + } +}); +TestSuite.prototype.runOnLoad = TestSuite.prototype.run_on_load +/* ------------------------------------------------------------------- */ + +var TestReporter = TestCaseUtil.create_class({ + ui_builder: null, + report_container: null, + current_test: null, + current_test_block: null, + current_test_done_bar: null, + seen_tests: null, + initialize: function(ui_builder){ + this.ui_builder = ui_builder || new TestReporter.UIBuilder(); + this.report_container = this.ui_builder.create_report_container(); + this._is_prepared = false; + this.seen_tests = []; + }, + register: function(test) { + this._check_prepare(); + this.seen_tests.push(test); + this.current_test = test; + this.current_test_block = this.ui_builder.create_test_block(test, this.report_container); + this.current_test_done_bar = this.ui_builder.create_done_bar(this.current_test_block); + this.current_test.__start_time = new Date().getTime(); + }, + test_started: function(test_name) {}, + test_passed: function(test_name) { + this.current_test_done_bar.style.width = ((this.current_test.passed / this.current_test.size) * 100)+'%'; + this.check_for_test_summary_report(); + }, + test_failed: function(test_name, e, assertion_num) { + this.ui_builder.create_error_report( + this.current_test_block, test_name, e, assertion_num, this.current_test[test_name].toString() + ); + this.check_for_test_summary_report(); + }, + check_for_test_summary_report: function() { + if (this.current_test.size == (this.current_test.passed + this.current_test.failed)) + this.ui_builder.create_summary_report(this.current_test_block, this.current_test); + this.ui_builder.update_overall_summary(this.report_container, this.seen_tests); + }, + prepare: function() {}, + _check_prepare: function() { + if(!this._is_prepared){ + this.prepare(); + this._is_prepared = true; + } + } +}); + +/* ------------------------------------------------------------------- */ + +TestReporter.UIBuilder = TestCaseUtil.create_class({ + DEFAULT_VIEW_NAME: 'summary-and-failed', + initialize: function() {}, + create_report_container: function() { + var block = document.createElement("DIV"); + block.innerHTML = ''+ + '
'+ + '

Test Report

'+ + ' '+ + '
    '+ + '
  • '+ + ' '+ + '
    Summary
    '+ + '
    '+ + '
    '+ + '
    '+ + '
    '+ + ' Total Cases: 0 / Tests: 0 / Passed: 0 / Failed : 0 / Assertions : 0'+ + '
    '+ + '
  • '+ + '
'+ + '
'; + this.init_view_switcher(block); + this.init_toggle_all_cases(block); + return block; + }, + create_test_block: function(test, report_container) { + var block = document.createElement("LI"); + var skipper = document.createElement("INPUT"); + skipper.type = "checkbox"; + skipper.title = "Skip/Activate Test"; + var $this = this; + skipper.onclick = function(event) { + $this.set_skip_test.apply($this, [event, test, skipper, block]); + }; + block.appendChild(skipper); + skipper.checked = !this.test_is_skipped(test); + skipper.disabled = !TestCaseUtil.Cookie.enabled(); + if (!skipper.checked) TestCaseUtil.Element.add_class_name(block, 'skipped'); + var test_name = document.createElement("DIV"); + test_name.className = "test-name"; + test_name.innerHTML = test.title; + block.appendChild(test_name); + TestCaseUtil.Array.find( + TestCaseUtil.Element.select(report_container, '*'), + function(element){ return element.id == 'test-cases-list';} + ).appendChild(block); + return block; + }, + create_done_bar: function(block) { + var progress_bar = document.createElement("DIV"); + progress_bar.className = "progress-bar"; + var done_bar = document.createElement("DIV"); + done_bar.className = "done-bar"; + progress_bar.appendChild(done_bar); + block.appendChild(progress_bar); + return done_bar; + }, + create_error_report: function(test_block, test_name, e, assertion_num, failed_test_code) { + var block = document.createElement("DIV"); + block.className = 'error-report-case'; + block.innerHTML = '
Test error in '+ test_name +' '+ + '('+this.get_assertion_name(e, assertion_num)+'):
'+this.create_error_text(e); + this.create_error_code_snippet(block, e, assertion_num, failed_test_code); + test_block.appendChild(block); + test_block.className = 'failed-case'; + return block; + }, + create_error_text: function(e) { + if (e instanceof TestCase.WrongValueException) { + var e_value = this._htmlized(e.format ? this._formatted_value(e.expected) : e.expected); + var r_value = this._htmlized(e.format ? this._formatted_value(e.received) : e.received); + return ''+ + ' '+ e_value +' '+ + ' '+ r_value +' '+ + this._create_diff_block(e) + + this._create_message_block(e); + } else if (e instanceof TestCase.ProblemException) + return ' '+this._htmlized(e.problem)+' ' + this._create_message_block(e); + else return e.message; + }, + _htmlized: function(value) { + return typeof(value) == 'string' ? value.replace(/\n/g, "
") : value; + }, + MAX_UNFOLDED_VALUE_LENGTH: 200, + _formatted_value: function(value) { + var value_s = TestCaseUtil.to_pretty_s(value); + if (value_s.length > this.MAX_UNFOLDED_VALUE_LENGTH) { + return ""+ + value_s.substr(0, this.MAX_UNFOLDED_VALUE_LENGTH).replace(/" + + ""+ + value_s.replace(/" + + 'Full version'; + } + return ""+ value_s.replace(/"; + }, + _create_diff_block: function (e) { + if (e.format) { + try { + var is_array = e.expected instanceof Array; + var diff = TestCaseUtil.Diff.calc(e.expected, e.received); + if (diff.length > 0) { + var diff_entries = []; + for (var i=0; i < diff.length; i++) { + for (var key in diff[i]) {} + var value = diff[i][key]; + if (typeof e.expected != 'string') { + if (typeof e.expected == 'object' && !is_array) { + var entries = []; + for (var name in value) { + entries.push('"'+ name +'": '+TestCaseUtil._one_level_to_s(value[name])); + } + value = "{"+ entries.join(",\n ") +"\n}"; + } else if (typeof e.expected != 'function') { + value = TestCaseUtil.debug(value); + } + } + diff_entries.push(''+ value.replace(/').replace(/ /g, ' ') +''); + } + return ''+ + ''+ + (is_array ? '[' : '')+ + diff_entries.join(is_array ? ',
 ' : '
') + + (is_array ? "
]" : '') + + '
'+ + 'Show'+ + '
'; + } + } catch (e) { } + } + return ''; + }, + _create_message_block: function(e) { + return e.message ? ' '+e.message+'' : ''; + }, + create_error_code_snippet: function(error_block, e, assertion_num, failed_test_code) { + var failed_test_code = this._paint_source_code(failed_test_code, e, assertion_num); + var failed_test_source_popup = document.createElement("DIV"); + failed_test_source_popup.className = 'failed-code-snippet'; + failed_test_source_popup.innerHTML = ''+failed_test_code+''; + failed_test_source_popup.style.display = 'none'; + var test_name_pointer = TestCaseUtil.Element.select(error_block, 'span.test-name')[0]; + test_name_pointer.parentNode.insertBefore(failed_test_source_popup, test_name_pointer); + TestCaseUtil.Event.observe(failed_test_source_popup, 'click', function(event) { TestCaseUtil.Event.stop(event); }); + TestCaseUtil.Event.observe(test_name_pointer, 'click', function(event) { + TestCaseUtil.Event.stop(event); + TestCaseUtil.Element.toggle(failed_test_source_popup); + }); + TestCaseUtil.Event.observe(document, 'click', function() { + TestCaseUtil.Element.hide(failed_test_source_popup); + }); + return failed_test_source_popup; + }, + _paint_source_code: function(failed_test_code, e, assertion_num) { + if (TestCaseUtil.Browser.IE) failed_test_code = failed_test_code.replace(/\n/mg, "
\n").replace(/ /mg, " "); + var assertion_num = assertion_num - 1; + var asserts_counter = 0; + var assertion_matches = failed_test_code.match(/this\.assert.+(\n|;)/ig); + if (assertion_matches) { + for (var i=0; i < assertion_matches.length; i++) { + match = assertion_matches[i]; + asserts_counter ++; + if (asserts_counter == assertion_num) + failed_test_code = failed_test_code.replace(match, ''+match+''); + } + failed_test_code = failed_test_code.replace(/(this)(\.)(assert[^\(]*)(\s*\()/img, '$1$2$3$4'); + } + return failed_test_code; + }, + create_summary_report: function(test_block, test) { + test.__taken_seconds = (test.__start_time && !test.skipped) ? (new Date().getTime() - test.__start_time)/1000 : 0; + var block = document.createElement("DIV"); + block.className = 'error-report-summary'; + block.innerHTML = "Total tests: "+ test.size + + " / Passed: "+ test.passed + + " / Failed: "+ test.failed + + " / Assertions: "+ test.assertions + + " / Seconds: "+test.__taken_seconds; + test_block.appendChild(block); + return block; + }, + update_overall_summary: function(block, cases_list) { + var cases = 0, tests = 0, passed = 0, failed = 0, assertions = 0, seconds = 0; + for (var i=0; i < cases_list.length; i++) { + var testcase = cases_list[i]; + if (!testcase.skipped) { + cases++; + tests += testcase.size; + passed += testcase.passed; + failed += testcase.failed; + assertions += testcase.assertions; + seconds += testcase.__taken_seconds; + } + } + var list_block = TestCaseUtil.Element.select(block, 'li#testcase-summary-block')[0]; + if (list_block) { + var done_bar_element = TestCaseUtil.Element.select(list_block, 'div.done-bar')[0]; + if (done_bar_element) + done_bar_element.style.width = ((tests > 0 ? (passed / tests) : 0) * 100)+'%'; + var summary_element = TestCaseUtil.Element.select(list_block, 'div.error-report-summary')[0]; + if (summary_element) + summary_element.innerHTML = "Total Cases: "+cases+ + " / Tests: "+tests+ + " / Passed: "+passed+ + " / Failed : "+failed+ + " / Assertions : "+assertions+ + " / Seconds: "+seconds.toString().substr(0,5); + } + if (cases > 0) { + var switcher = TestCaseUtil.Element.select(block, 'input#testcase-toggle-all-cases')[0]; + if (switcher) + switcher.checked = true; + } + }, + test_is_skipped: function(test) { + return TestCaseUtil.Array.include(this.get_skipped_cases(), escape(test.title)); + }, + get_assertion_name: function(e, assertion_num) { + if (e.test && e.assertion) { + for (key in e.test) + if (e.test[key] == e.assertion) + return key; + } else { + return "assertion #"+assertion_num; + } + }, + init_view_switcher: function(block) { + var descendants = TestCaseUtil.Element.select(block, '*'); + var switcher = TestCaseUtil.Array.find(descendants, function(el) { return el.id == 'testcase-view-switcher' }); + var list_element = TestCaseUtil.Array.find(descendants, function(el) { return el.id == 'test-cases-list' }); + if (switcher && list_element) { + TestCaseUtil.Event.observe(switcher, 'change', function() { + list_element.className = switcher.value; + TestCaseUtil.Cookie.set("testcase_view", switcher.value, 100); + }); + switcher.value = this.get_current_view_name(); + list_element.className = switcher.value; + } + }, + get_current_view_name: function() { + var view_name = TestCaseUtil.Cookie.get('testcase_view'); + return view_name == '' ? this.DEFAULT_VIEW_NAME : view_name; + }, + init_toggle_all_cases: function(block) { + var checkbox = TestCaseUtil.Array.find(TestCaseUtil.Element.select(block, '*'), function(el) { return el.id == 'testcase-toggle-all-cases' }); + if (checkbox) { + var $this = this; + TestCaseUtil.Event.observe(checkbox, 'click', function(event) { + $this.toggle_all_cases.apply($this, [this, block, checkbox]); + }); + checkbox.checked = false; + checkbox.disabled = TestCaseUtil.Cookie.enabled() ? false : true; + } + }, + toggle_all_cases: function(event, block, checkbox) { + var elements = TestCaseUtil.Element.select(block, 'ul li input[type="checkbox"]'); + for (var i=0; i < elements.length; i++) { + elements[i].checked = checkbox.checked; + if (elements[i].onclick) + elements[i].onclick(); + } + }, + set_skip_test: function(event, test, checkbox, block) { + TestCaseUtil.Element[checkbox.checked ? 'remove_class_name' : 'add_class_name'](block, 'skipped'); + var skipped_cases = this.get_skipped_cases(); + if (checkbox.checked) + skipped_cases = TestCaseUtil.Array.without(skipped_cases, escape(test.title)); + else + skipped_cases.push(escape(test.title)); + TestCaseUtil.Cookie.set("testcase_skipped_cases", skipped_cases.join("|")); + }, + get_skipped_cases: function() { + return TestCaseUtil.Cookie.get('testcase_skipped_cases').split("|"); + } +}); + +/* ------------------------------------------------------------------- */ + +TestReporter.InlineReporter = TestCaseUtil.create_class(TestReporter, { + prepare: function() { + var source = ''+ + '
'+ + '
'+ + '
x
'+ + '
'+ + '
'+ + '
'+ + ''; + if (document.body) { + var block = document.createElement("DIV"); + block.innerHTML = source; + document.body.appendChild(block); + } else { + document.write(source); + } + TestCaseUtil.Element.down('testcase-inline-report-block', 'div.body').appendChild(this.report_container); + } +}); +/* ------------------------------------------------------------------- */ + +TestCaseUtil.extend(TestCase, { + reporter: new TestReporter.InlineReporter() +}); + + +/* ---------------- THE STYLES COLLECTION DUMPING ------------------- */ +document.write(''); \ No newline at end of file diff --git a/addons/Core/partials/layout-editor.inc b/addons/Core/partials/layout-editor.inc new file mode 100644 index 0000000..ccfa44c --- /dev/null +++ b/addons/Core/partials/layout-editor.inc @@ -0,0 +1,8 @@ +
+
+ +
+
+ \ No newline at end of file