diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3858cc7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.log +*.pid +tmp* +.DS_Store +*~ +pkg/ +*.swp +build/ \ No newline at end of file diff --git a/frameworks/jspec/images/bg.png b/frameworks/jspec/images/bg.png new file mode 100644 index 0000000..947804f Binary files /dev/null and b/frameworks/jspec/images/bg.png differ diff --git a/frameworks/jspec/images/hr.png b/frameworks/jspec/images/hr.png new file mode 100644 index 0000000..4a94d12 Binary files /dev/null and b/frameworks/jspec/images/hr.png differ diff --git a/frameworks/jspec/images/loading.gif b/frameworks/jspec/images/loading.gif new file mode 100644 index 0000000..c69e937 Binary files /dev/null and b/frameworks/jspec/images/loading.gif differ diff --git a/frameworks/jspec/images/sprites.bg.png b/frameworks/jspec/images/sprites.bg.png new file mode 100644 index 0000000..dc8790f Binary files /dev/null and b/frameworks/jspec/images/sprites.bg.png differ diff --git a/frameworks/jspec/images/sprites.png b/frameworks/jspec/images/sprites.png new file mode 100644 index 0000000..010b98e Binary files /dev/null and b/frameworks/jspec/images/sprites.png differ diff --git a/frameworks/jspec/images/vr.png b/frameworks/jspec/images/vr.png new file mode 100644 index 0000000..b2e7617 Binary files /dev/null and b/frameworks/jspec/images/vr.png differ diff --git a/frameworks/jspec/jspec.css b/frameworks/jspec/jspec.css new file mode 100644 index 0000000..629d41c --- /dev/null +++ b/frameworks/jspec/jspec.css @@ -0,0 +1,149 @@ +body.jspec { + margin: 45px 0; + font: 12px "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; + background: #efefef url(images/bg.png) top left repeat-x; + text-align: center; +} +#jspec { + margin: 0 auto; + padding-top: 30px; + width: 1008px; + background: url(images/vr.png) top left repeat-y; + text-align: left; +} +#jspec-top { + position: relative; + margin: 0 auto; + width: 1008px; + height: 40px; + background: url(images/sprites.bg.png) top left no-repeat; +} +#jspec-bottom { + margin: 0 auto; + width: 1008px; + height: 15px; + background: url(images/sprites.bg.png) bottom left no-repeat; +} +#jspec .loading { + margin-top: -45px; + width: 1008px; + height: 80px; + background: url(images/loading.gif) 50% 50% no-repeat; +} +#jspec-title { + position: absolute; + top: 15px; + left: 20px; + width: 160px; + font-size: 22px; + font-weight: normal; + background: url(images/sprites.png) 0 -126px no-repeat; + text-align: center; +} +#jspec-title em { + font-size: 10px; + font-style: normal; + color: #BCC8D1; +} +#jspec-report * { + margin: 0; + padding: 0; + background: none; + border: none; +} +#jspec-report { + padding: 15px 40px; + font: 11px "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; + color: #7B8D9B; +} +#jspec-report.has-failures { + padding-bottom: 30px; +} +#jspec-report .hidden { + display: none; +} +#jspec-report .heading { + margin-bottom: 15px; +} +#jspec-report .heading span { + padding-right: 10px; +} +#jspec-report .heading .passes em { + color: #0ea0eb; +} +#jspec-report .heading .failures em { + color: #FA1616; +} +#jspec-report table { + font-size: 11px; + border-collapse: collapse; +} +#jspec-report td { + padding: 8px; + text-indent: 30px; + color: #7B8D9B; +} +#jspec-report tr.body { + display: none; +} +#jspec-report tr.body pre { + margin: 0; + padding: 0 0 5px 25px; +} +#jspec-report tr.even:hover + tr.body, +#jspec-report tr.odd:hover + tr.body { + display: block; +} +#jspec-report tr td:first-child em { + display: block; + clear: both; + font-style: normal; + font-weight: normal; + color: #7B8D9B; +} +#jspec-report tr.even:hover, +#jspec-report tr.odd:hover { + text-shadow: 1px 1px 1px #fff; + background: #F2F5F7; +} +#jspec-report td + td { + padding-right: 0; + width: 15px; +} +#jspec-report td.pass { + background: url(images/sprites.png) 3px -7px no-repeat; +} +#jspec-report td.fail { + background: url(images/sprites.png) 3px -158px no-repeat; + font-weight: bold; + color: #FC0D0D; +} +#jspec-report td.requires-implementation { + background: url(images/sprites.png) 3px -333px no-repeat; +} +#jspec-report tr.description td { + margin-top: 25px; + padding-top: 25px; + font-size: 12px; + font-weight: bold; + text-indent: 0; + color: #1a1a1a; +} +#jspec-report tr.description:first-child td { + border-top: none; +} +#jspec-report .assertion { + display: block; + float: left; + margin: 0 0 0 1px; + padding: 0; + width: 1px; + height: 5px; + background: #7B8D9B; +} +#jspec-report .assertion.failed { + background: red; +} +.jspec-sandbox { + display: none; +} \ No newline at end of file diff --git a/frameworks/jspec/jspec.growl.js b/frameworks/jspec/jspec.growl.js new file mode 100644 index 0000000..a150257 --- /dev/null +++ b/frameworks/jspec/jspec.growl.js @@ -0,0 +1,115 @@ + +// JSpec - Growl - Copyright TJ Holowaychuk (MIT Licensed) + +;(function(){ + + Growl = { + + // --- Version + + version: '1.0.0', + + /** + * Execute the given _cmd_, returning an array of lines from stdout. + * + * Examples: + * + * Growl.exec('growlnotify', '-m', msg) + * + * @param {string ...} cmd + * @return {array} + * @api public + */ + + exec: function(cmd) { + var lines = [], line + with (JavaImporter(java.lang, java.io)) { + var proccess = Runtime.getRuntime().exec(Array.prototype.slice.call(arguments)) + var stream = new DataInputStream(proccess.getInputStream()) + while (line = stream.readLine()) + lines.push(line + '') + stream.close() + } + return lines + }, + + /** + * Return the extension of the given _path_ or null. + * + * @param {string} path + * @return {string} + * @api private + */ + + extname: function(path) { + return path.lastIndexOf('.') != -1 ? + path.slice(path.lastIndexOf('.') + 1, path.length) : + null + }, + + /** + * Version of the 'growlnotify' binary. + * + * @return {string} + * @api private + */ + + binVersion: function() { + try { return this.exec('growlnotify', '-v')[0].split(' ')[1] } catch (e) {} + }, + + /** + * Send growl notification _msg_ with _options_. + * + * Options: + * + * - title Notification title + * - sticky Make the notification stick (defaults to false) + * - name Application name (defaults to growlnotify) + * - image + * - path to an icon sets --iconpath + * - path to an image sets --image + * - capitalized word sets --appIcon + * - filename uses extname as --icon + * - otherwise treated as --icon + * + * Examples: + * + * Growl.notify('New email') + * Growl.notify('5 new emails', { title: 'Thunderbird' }) + * + * @param {string} msg + * @param {options} hash + * @api public + */ + + notify: function(msg, options) { + options = options || {} + var args = ['growlnotify', '-m', msg] + if (!this.binVersion()) throw new Error('growlnotify executable is required') + if (image = options.image) { + var flag, ext = this.extname(image) + flag = flag || ext == 'icns' && 'iconpath' + flag = flag || /^[A-Z]/.test(image) && 'appIcon' + flag = flag || /^png|gif|jpe?g$/.test(ext) && 'image' + flag = flag || ext && (image = ext) && 'icon' + flag = flag || 'icon' + args.push('--' + flag, image) + } + if (options.sticky) args.push('--sticky') + if (options.name) args.push('--name', options.name) + if (options.title) args.push(options.title) + this.exec.apply(this, args) + } + } + + JSpec.include({ + name: 'Growl', + reporting: function(options){ + var stats = JSpec.stats + if (stats.failures) Growl.notify('failed ' + stats.failures + ' assertions', { title: 'JSpec'}) + else Growl.notify('passed ' + stats.passes + ' assertions', { title: 'JSpec' }) + } + }) + +})() \ No newline at end of file diff --git a/frameworks/jspec/jspec.jquery.js b/frameworks/jspec/jspec.jquery.js new file mode 100644 index 0000000..fcad7ab --- /dev/null +++ b/frameworks/jspec/jspec.jquery.js @@ -0,0 +1,72 @@ + +// JSpec - jQuery - Copyright TJ Holowaychuk (MIT Licensed) + +JSpec +.requires('jQuery', 'when using jspec.jquery.js') +.include({ + name: 'jQuery', + + // --- Initialize + + init : function() { + jQuery.ajaxSetup({ async: false }) + }, + + // --- Utilities + + utilities : { + element: jQuery, + elements: jQuery, + sandbox : function() { + return jQuery('
') + } + }, + + // --- Matchers + + matchers : { + have_tag : "jQuery(expected, actual).length === 1", + have_one : "alias have_tag", + have_tags : "jQuery(expected, actual).length > 1", + have_many : "alias have_tags", + have_any : "alias have_tags", + have_child : "jQuery(actual).children(expected).length === 1", + have_children : "jQuery(actual).children(expected).length > 1", + have_text : "jQuery(actual).text() === expected", + have_value : "jQuery(actual).val() === expected", + be_enabled : "!jQuery(actual).attr('disabled')", + have_class : "jQuery(actual).hasClass(expected)", + + be_visible : function(actual) { + return jQuery(actual).css('display') != 'none' && + jQuery(actual).css('visibility') != 'hidden' && + jQuery(actual).attr('type') != 'hidden' + }, + + be_hidden : function(actual) { + return !JSpec.does(actual, 'be_visible') + }, + + have_classes : function(actual) { + return !JSpec.any(JSpec.toArray(arguments, 1), function(arg){ + return !JSpec.does(actual, 'have_class', arg) + }) + }, + + have_attr : function(actual, attr, value) { + return value ? jQuery(actual).attr(attr) == value: + jQuery(actual).attr(attr) + }, + + 'be disabled selected checked' : function(attr) { + return 'jQuery(actual).attr("' + attr + '")' + }, + + 'have type id title alt href src sel rev name target' : function(attr) { + return function(actual, value) { + return JSpec.does(actual, 'have_attr', attr, value) + } + } + } +}) + diff --git a/frameworks/jspec/jspec.js b/frameworks/jspec/jspec.js new file mode 100644 index 0000000..d6daf5e --- /dev/null +++ b/frameworks/jspec/jspec.js @@ -0,0 +1,1756 @@ + +// JSpec - Core - Copyright TJ Holowaychuk (MIT Licensed) + +;(function(){ + + JSpec = { + version : '3.3.2', + assert : true, + cache : {}, + suites : [], + modules : [], + allSuites : [], + matchers : {}, + stubbed : [], + options : {}, + request : 'XMLHttpRequest' in this ? XMLHttpRequest : null, + stats : { specs: 0, assertions: 0, failures: 0, passes: 0, specsFinished: 0, suitesFinished: 0 }, + + /** + * Default context in which bodies are evaluated. + * + * Replace context simply by setting JSpec.context + * to your own like below: + * + * JSpec.context = { foo : 'bar' } + * + * Contexts can be changed within any body, this can be useful + * in order to provide specific helper methods to specific suites. + * + * To reset (usually in after hook) simply set to null like below: + * + * JSpec.context = null + * + */ + + defaultContext : { + + /** + * Return an object used for proxy assertions. + * This object is used to indicate that an object + * should be an instance of _object_, not the constructor + * itself. + * + * @param {function} constructor + * @return {hash} + * @api public + */ + + an_instance_of : function(constructor) { + return { an_instance_of : constructor } + }, + + /** + * Load fixture at _path_. + * + * Fixtures are resolved as: + * + * - + * - .html + * + * @param {string} path + * @return {string} + * @api public + */ + + fixture : function(path) { + if (JSpec.cache[path]) return JSpec.cache[path] + return JSpec.cache[path] = + JSpec.tryLoading(JSpec.options.fixturePath + '/' + path) || + JSpec.tryLoading(JSpec.options.fixturePath + '/' + path + '.html') + } + }, + + // --- Objects + + reporters : { + + /** + * Report to server. + * + * Options: + * - uri specific uri to report to. + * - verbose weither or not to output messages + * - failuresOnly output failure messages only + * + * @api public + */ + + Server : function(results, options) { + var uri = options.uri || 'http://' + window.location.host + '/results' + JSpec.post(uri, { + stats: JSpec.stats, + options: options, + results: map(results.allSuites, function(suite) { + if (suite.hasSpecs()) + return { + description: suite.description, + specs: map(suite.specs, function(spec) { + return { + description: spec.description, + message: !spec.passed() ? spec.failure().message : null, + status: spec.requiresImplementation() ? 'pending' : + spec.passed() ? 'pass' : + 'fail', + assertions: map(spec.assertions, function(assertion){ + return { + passed: assertion.passed + } + }) + } + }) + } + }) + }) + if ('close' in main) main.close() + }, + + /** + * Default reporter, outputting to the DOM. + * + * Options: + * - reportToId id of element to output reports to, defaults to 'jspec' + * - failuresOnly displays only suites with failing specs + * + * @api public + */ + + DOM : function(results, options) { + var id = option('reportToId') || 'jspec', + report = document.getElementById(id), + failuresOnly = option('failuresOnly'), + classes = results.stats.failures ? 'has-failures' : '' + if (!report) throw 'JSpec requires the element #' + id + ' to output its reports' + + function bodyContents(body) { + return JSpec. + escape(JSpec.contentsOf(body)). + replace(/^ */gm, function(a){ return (new Array(Math.round(a.length / 3))).join(' ') }). + replace(/\r\n|\r|\n/gm, '
') + } + + report.innerHTML = '
\ + Passes: ' + results.stats.passes + ' \ + Failures: ' + results.stats.failures + ' \ + Duration: ' + results.duration + ' ms \ +
' + map(results.allSuites, function(suite) { + var displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran + if (displaySuite && suite.hasSpecs()) + return '' + + map(suite.specs, function(i, spec) { + return '' + + (spec.requiresImplementation() ? + '' : + (spec.passed() && !failuresOnly) ? + '' : + !spec.passed() ? + '' : + '') + + '' + }).join('') + '' + }).join('') + '
' + escape(suite.description) + '
' + escape(spec.description) + '' + escape(spec.description)+ '' + spec.assertionsGraph() + '' + escape(spec.description) + + map(spec.failures(), function(a){ return '' + escape(a.message) + '' }).join('') + + '' + spec.assertionsGraph() + '
' + bodyContents(spec.body) + '
' + }, + + /** + * Terminal reporter. + * + * @api public + */ + + Terminal : function(results, options) { + var failuresOnly = option('failuresOnly') + print(color("\n Passes: ", 'bold') + color(results.stats.passes, 'green') + + color(" Failures: ", 'bold') + color(results.stats.failures, 'red') + + color(" Duration: ", 'bold') + color(results.duration, 'green') + " ms \n") + + function indent(string) { + return string.replace(/^(.)/gm, ' $1') + } + + each(results.allSuites, function(suite) { + var displaySuite = failuresOnly ? suite.ran && !suite.passed() : suite.ran + if (displaySuite && suite.hasSpecs()) { + print(color(' ' + suite.description, 'bold')) + each(suite.specs, function(spec){ + var assertionsGraph = inject(spec.assertions, '', function(graph, assertion){ + return graph + color('.', assertion.passed ? 'green' : 'red') + }) + if (spec.requiresImplementation()) + print(color(' ' + spec.description, 'blue') + assertionsGraph) + else if (spec.passed() && !failuresOnly) + print(color(' ' + spec.description, 'green') + assertionsGraph) + else if (!spec.passed()) + print(color(' ' + spec.description, 'red') + assertionsGraph + + "\n" + indent(map(spec.failures(), function(a){ return a.message }).join("\n")) + "\n") + }) + print("") + } + }) + + quit(results.stats.failures) + } + }, + + Assertion : function(matcher, actual, expected, negate) { + extend(this, { + message: '', + passed: false, + actual: actual, + negate: negate, + matcher: matcher, + expected: expected, + + // Report assertion results + + report : function() { + if (JSpec.assert) + this.passed ? JSpec.stats.passes++ : JSpec.stats.failures++ + return this + }, + + // Run the assertion + + run : function() { + // TODO: remove unshifting + expected.unshift(actual) + this.result = matcher.match.apply(this, expected) + this.passed = negate ? !this.result : this.result + if (!this.passed) this.message = matcher.message.call(this, actual, expected, negate, matcher.name) + return this + } + }) + }, + + ProxyAssertion : function(object, method, times, negate) { + var self = this + var old = object[method] + + // Proxy + + object[method] = function(){ + args = toArray(arguments) + result = old.apply(object, args) + self.calls.push({ args : args, result : result }) + return result + } + + // Times + + this.times = { + once : 1, + twice : 2 + }[times] || times || 1 + + extend(this, { + calls: [], + message: '', + defer: true, + passed: false, + negate: negate, + object: object, + method: method, + + // Proxy return value + + and_return : function(result) { + this.expectedResult = result + return this + }, + + // Proxy arguments passed + + with_args : function() { + this.expectedArgs = toArray(arguments) + return this + }, + + // Check if any calls have failing results + + anyResultsFail : function() { + return any(this.calls, function(call){ + return self.expectedResult.an_instance_of ? + call.result.constructor != self.expectedResult.an_instance_of: + !equal(self.expectedResult, call.result) + }) + }, + + // Check if any calls have passing results + + anyResultsPass : function() { + return any(this.calls, function(call){ + return self.expectedResult.an_instance_of ? + call.result.constructor == self.expectedResult.an_instance_of: + equal(self.expectedResult, call.result) + }) + }, + + // Return the passing result + + passingResult : function() { + return this.anyResultsPass().result + }, + + // Return the failing result + + failingResult : function() { + return this.anyResultsFail().result + }, + + // Check if any arguments fail + + anyArgsFail : function() { + return any(this.calls, function(call){ + return any(self.expectedArgs, function(i, arg){ + if (arg == null) return call.args[i] == null + return arg.an_instance_of ? + call.args[i].constructor != arg.an_instance_of: + !equal(arg, call.args[i]) + + }) + }) + }, + + // Check if any arguments pass + + anyArgsPass : function() { + return any(this.calls, function(call){ + return any(self.expectedArgs, function(i, arg){ + return arg.an_instance_of ? + call.args[i].constructor == arg.an_instance_of: + equal(arg, call.args[i]) + + }) + }) + }, + + // Return the passing args + + passingArgs : function() { + return this.anyArgsPass().args + }, + + // Return the failing args + + failingArgs : function() { + return this.anyArgsFail().args + }, + + // Report assertion results + + report : function() { + if (JSpec.assert) + this.passed ? ++JSpec.stats.passes : ++JSpec.stats.failures + return this + }, + + // Run the assertion + + run : function() { + var methodString = 'expected ' + object.toString() + '.' + method + '()' + (negate ? ' not' : '' ) + + function times(n) { + return n > 2 ? n + ' times' : { 1: 'once', 2: 'twice' }[n] + } + + if (this.expectedResult != null && (negate ? this.anyResultsPass() : this.anyResultsFail())) + this.message = methodString + ' to return ' + puts(this.expectedResult) + + ' but ' + (negate ? 'it did' : 'got ' + puts(this.failingResult())) + + if (this.expectedArgs && (negate ? !this.expectedResult && this.anyArgsPass() : this.anyArgsFail())) + this.message = methodString + ' to be called with ' + puts.apply(this, this.expectedArgs) + + ' but was' + (negate ? '' : ' called with ' + puts.apply(this, this.failingArgs())) + + if (negate ? !this.expectedResult && !this.expectedArgs && this.calls.length >= this.times : this.calls.length != this.times) + this.message = methodString + ' to be called ' + times(this.times) + + ', but ' + (this.calls.length == 0 ? ' was not called' : ' was called ' + times(this.calls.length)) + + if (!this.message.length) + this.passed = true + + return this + } + }) + }, + + /** + * Specification Suite block object. + * + * @param {string} description + * @param {function} body + * @api private + */ + + Suite : function(description, body) { + var self = this + extend(this, { + body: body, + description: description, + suites: [], + specs: [], + ran: false, + hooks: { 'before' : [], 'after' : [], 'before_each' : [], 'after_each' : [] }, + + // Add a spec to the suite + + addSpec : function(description, body) { + var spec = new JSpec.Spec(description, body) + this.specs.push(spec) + JSpec.stats.specs++ // TODO: abstract + spec.suite = this + }, + + // Add a hook to the suite + + addHook : function(hook, body) { + this.hooks[hook].push(body) + }, + + // Add a nested suite + + addSuite : function(description, body) { + var suite = new JSpec.Suite(description, body) + JSpec.allSuites.push(suite) + suite.name = suite.description + suite.description = this.description + ' ' + suite.description + this.suites.push(suite) + suite.suite = this + }, + + // Invoke a hook in context to this suite + + hook : function(hook) { + if (this.suite) this.suite.hook(hook) + each(this.hooks[hook], function(body) { + JSpec.evalBody(body, "Error in hook '" + hook + "', suite '" + self.description + "': ") + }) + }, + + // Check if nested suites are present + + hasSuites : function() { + return this.suites.length + }, + + // Check if this suite has specs + + hasSpecs : function() { + return this.specs.length + }, + + // Check if the entire suite passed + + passed : function() { + return !any(this.specs, function(spec){ + return !spec.passed() + }) + } + }) + }, + + /** + * Specification block object. + * + * @param {string} description + * @param {function} body + * @api private + */ + + Spec : function(description, body) { + extend(this, { + body: body, + description: description, + assertions: [], + + // Add passing assertion + + pass : function(message) { + this.assertions.push({ passed: true, message: message }) + if (JSpec.assert) ++JSpec.stats.passes + }, + + // Add failing assertion + + fail : function(message) { + this.assertions.push({ passed: false, message: message }) + if (JSpec.assert) ++JSpec.stats.failures + }, + + // Run deferred assertions + + runDeferredAssertions : function() { + each(this.assertions, function(assertion){ + if (assertion.defer) assertion.run().report(), hook('afterAssertion', assertion) + }) + }, + + // Find first failing assertion + + failure : function() { + return find(this.assertions, function(assertion){ + return !assertion.passed + }) + }, + + // Find all failing assertions + + failures : function() { + return select(this.assertions, function(assertion){ + return !assertion.passed + }) + }, + + // Weither or not the spec passed + + passed : function() { + return !this.failure() + }, + + // Weither or not the spec requires implementation (no assertions) + + requiresImplementation : function() { + return this.assertions.length == 0 + }, + + // Sprite based assertions graph + + assertionsGraph : function() { + return map(this.assertions, function(assertion){ + return '' + }).join('') + } + }) + }, + + Module : function(methods) { + extend(this, methods) + }, + + JSON : { + + /** + * Generic sequences. + */ + + meta : { + '\b' : '\\b', + '\t' : '\\t', + '\n' : '\\n', + '\f' : '\\f', + '\r' : '\\r', + '"' : '\\"', + '\\' : '\\\\' + }, + + /** + * Escapable sequences. + */ + + escapable : /[\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + + /** + * JSON encode _object_. + * + * @param {mixed} object + * @return {string} + * @api private + */ + + encode : function(object) { + var self = this + if (object == undefined || object == null) return 'null' + if (object === true) return 'true' + if (object === false) return 'false' + switch (typeof object) { + case 'number': return object + case 'string': return this.escapable.test(object) ? + '"' + object.replace(this.escapable, function (a) { + return typeof self.meta[a] === 'string' ? self.meta[a] : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4) + }) + '"' : + '"' + object + '"' + case 'object': + if (object.constructor == Array) + return '[' + map(object, function(val){ + return self.encode(val) + }).join(', ') + ']' + else if (object) + return '{' + map(object, function(key, val){ + return self.encode(key) + ':' + self.encode(val) + }).join(', ') + '}' + } + return 'null' + } + }, + + // --- DSLs + + DSLs : { + snake : { + expect : function(actual){ + return JSpec.expect(actual) + }, + + describe : function(description, body) { + return JSpec.currentSuite.addSuite(description, body) + }, + + it : function(description, body) { + return JSpec.currentSuite.addSpec(description, body) + }, + + before : function(body) { + return JSpec.currentSuite.addHook('before', body) + }, + + after : function(body) { + return JSpec.currentSuite.addHook('after', body) + }, + + before_each : function(body) { + return JSpec.currentSuite.addHook('before_each', body) + }, + + after_each : function(body) { + return JSpec.currentSuite.addHook('after_each', body) + }, + + should_behave_like : function(description) { + return JSpec.shareBehaviorsOf(description) + } + } + }, + + // --- Methods + + /** + * Check if _value_ is 'stop'. For use as a + * utility callback function. + * + * @param {mixed} value + * @return {bool} + * @api public + */ + + haveStopped : function(value) { + return value === 'stop' + }, + + /** + * Include _object_ which may be a hash or Module instance. + * + * @param {hash, Module} object + * @return {JSpec} + * @api public + */ + + include : function(object) { + var module = object.constructor == JSpec.Module ? object : new JSpec.Module(object) + this.modules.push(module) + if ('init' in module) module.init() + if ('utilities' in module) extend(this.defaultContext, module.utilities) + if ('matchers' in module) this.addMatchers(module.matchers) + if ('reporters' in module) extend(this.reporters, module.reporters) + if ('DSLs' in module) + each(module.DSLs, function(name, methods){ + JSpec.DSLs[name] = JSpec.DSLs[name] || {} + extend(JSpec.DSLs[name], methods) + }) + return this + }, + + /** + * Add a module hook _name_, which is immediately + * called per module with the _args_ given. An array of + * hook return values is returned. + * + * @param {name} string + * @param {...} args + * @return {array} + * @api private + */ + + hook : function(name, args) { + args = toArray(arguments, 1) + return inject(JSpec.modules, [], function(results, module){ + if (typeof module[name] == 'function') + results.push(JSpec.evalHook(module, name, args)) + }) + }, + + /** + * Eval _module_ hook _name_ with _args_. Evaluates in context + * to the module itself, JSpec, and JSpec.context. + * + * @param {Module} module + * @param {string} name + * @param {array} args + * @return {mixed} + * @api private + */ + + evalHook : function(module, name, args) { + hook('evaluatingHookBody', module, name) + try { return module[name].apply(module, args) } + catch(e) { error('Error in hook ' + module.name + '.' + name + ': ', e) } + }, + + /** + * Same as hook() however accepts only one _arg_ which is + * considered immutable. This function passes the arg + * to the first module, then passes the return value of the last + * module called, to the following module. + * + * @param {string} name + * @param {mixed} arg + * @return {mixed} + * @api private + */ + + hookImmutable : function(name, arg) { + return inject(JSpec.modules, arg, function(result, module){ + if (typeof module[name] == 'function') + return JSpec.evalHook(module, name, [result]) + }) + }, + + /** + * Find a suite by its description or name. + * + * @param {string} description + * @return {Suite} + * @api private + */ + + findSuite : function(description) { + return find(this.allSuites, function(suite){ + return suite.name == description || suite.description == description + }) + }, + + /** + * Share behaviors (specs) of the given suite with + * the current suite. + * + * @param {string} description + * @api public + */ + + shareBehaviorsOf : function(description) { + if (suite = this.findSuite(description)) this.copySpecs(suite, this.currentSuite) + else throw 'failed to share behaviors. ' + puts(description) + ' is not a valid Suite name' + }, + + /** + * Copy specs from one suite to another. + * + * @param {Suite} fromSuite + * @param {Suite} toSuite + * @api public + */ + + copySpecs : function(fromSuite, toSuite) { + each(fromSuite.specs, function(spec){ + var newSpec = new Object(); + extend(newSpec, spec); + newSpec.assertions = []; + toSuite.specs.push(newSpec); + }) + }, + + /** + * Convert arguments to an array. + * + * @param {object} arguments + * @param {int} offset + * @return {array} + * @api public + */ + + toArray : function(arguments, offset) { + return Array.prototype.slice.call(arguments, offset || 0) + }, + + /** + * Return ANSI-escaped colored string. + * + * @param {string} string + * @param {string} color + * @return {string} + * @api public + */ + + color : function(string, color) { + return "\u001B[" + { + bold : 1, + black : 30, + red : 31, + green : 32, + yellow : 33, + blue : 34, + magenta : 35, + cyan : 36, + white : 37 + }[color] + 'm' + string + "\u001B[0m" + }, + + /** + * Default matcher message callback. + * + * @api private + */ + + defaultMatcherMessage : function(actual, expected, negate, name) { + return 'expected ' + puts(actual) + ' to ' + + (negate ? 'not ' : '') + + name.replace(/_/g, ' ') + + ' ' + (expected.length > 1 ? + puts.apply(this, expected.slice(1)) : + '') + }, + + /** + * Normalize a matcher message. + * + * When no messge callback is present the defaultMatcherMessage + * will be assigned, will suffice for most matchers. + * + * @param {hash} matcher + * @return {hash} + * @api public + */ + + normalizeMatcherMessage : function(matcher) { + if (typeof matcher.message != 'function') + matcher.message = this.defaultMatcherMessage + return matcher + }, + + /** + * Normalize a matcher body + * + * This process allows the following conversions until + * the matcher is in its final normalized hash state. + * + * - '==' becomes 'actual == expected' + * - 'actual == expected' becomes 'return actual == expected' + * - function(actual, expected) { return actual == expected } becomes + * { match : function(actual, expected) { return actual == expected }} + * + * @param {mixed} body + * @return {hash} + * @api public + */ + + normalizeMatcherBody : function(body) { + switch (body.constructor) { + case String: + if (captures = body.match(/^alias (\w+)/)) return JSpec.matchers[last(captures)] + if (body.length < 4) body = 'actual ' + body + ' expected' + return { match: function(actual, expected) { return eval(body) }} + + case Function: + return { match: body } + + default: + return body + } + }, + + /** + * Get option value. This method first checks if + * the option key has been set via the query string, + * otherwise returning the options hash value. + * + * @param {string} key + * @return {mixed} + * @api public + */ + + option : function(key) { + return (value = query(key)) !== null ? value : + JSpec.options[key] || null + }, + + /** + * Check if object _a_, is equal to object _b_. + * + * @param {object} a + * @param {object} b + * @return {bool} + * @api private + */ + + equal: function(a, b) { + if (typeof a != typeof b) return + if (a === b) return true + if (a instanceof RegExp) + return a.toString() === b.toString() + if (a instanceof Date) + return Number(a) === Number(b) + if (typeof a != 'object') return + if (a.length !== undefined) + if (a.length !== b.length) return + else + for (var i = 0, len = a.length; i < len; ++i) + if (!equal(a[i], b[i])) + return + for (var key in a) + if (!equal(a[key], b[key])) + return + return true + }, + + /** + * Return last element of an array. + * + * @param {array} array + * @return {object} + * @api public + */ + + last : function(array) { + return array[array.length - 1] + }, + + /** + * Convert object(s) to a print-friend string. + * + * @param {...} object + * @return {string} + * @api public + */ + + puts : function(object) { + if (arguments.length > 1) + return map(toArray(arguments), function(arg){ + return puts(arg) + }).join(', ') + if (object === undefined) return 'undefined' + if (object === null) return 'null' + if (object === true) return 'true' + if (object === false) return 'false' + if (object.an_instance_of) return 'an instance of ' + object.an_instance_of.name + if (object.jquery && object.selector.length > 0) return 'selector ' + puts(object.selector) + if (object.jquery) return object.get(0).outerHTML + if (object.nodeName) return object.outerHTML + switch (object.constructor) { + case Function: return object.name || object + case String: + return '"' + object + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\t/g, '\\t') + + '"' + case Array: + return inject(object, '[', function(b, v){ + return b + ', ' + puts(v) + }).replace('[,', '[') + ' ]' + case Object: + object.__hit__ = true + return inject(object, '{', function(b, k, v) { + if (k == '__hit__') return b + return b + ', ' + k + ': ' + (v && v.__hit__ ? '' : puts(v)) + }).replace('{,', '{') + ' }' + default: + return object.toString() + } + }, + + /** + * Escape HTML. + * + * @param {string} html + * @return {string} + * @api public + */ + + escape : function(html) { + return html.toString() + .replace(/&/gmi, '&') + .replace(/"/gmi, '"') + .replace(/>/gmi, '>') + .replace(/ current) while (++current <= end) values.push(current) + else while (--current >= end) values.push(current) + return '[' + values + ']' + }, + + /** + * Report on the results. + * + * @api public + */ + + report : function() { + this.duration = Number(new Date) - this.start + hook('reporting', JSpec.options) + new (JSpec.options.reporter || JSpec.reporters.DOM)(JSpec, JSpec.options) + }, + + /** + * Run the spec suites. Options are merged + * with JSpec options when present. + * + * @param {hash} options + * @return {JSpec} + * @api public + */ + + run : function(options) { + if (any(hook('running'), haveStopped)) return this + if (options) extend(this.options, options) + this.start = Number(new Date) + each(this.suites, function(suite) { JSpec.runSuite(suite) }) + return this + }, + + /** + * Run a suite. + * + * @param {Suite} suite + * @api public + */ + + runSuite : function(suite) { + this.currentSuite = suite + this.evalBody(suite.body) + suite.ran = true + hook('beforeSuite', suite), suite.hook('before') + each(suite.specs, function(spec) { + hook('beforeSpec', spec) + suite.hook('before_each') + JSpec.runSpec(spec) + hook('afterSpec', spec) + suite.hook('after_each') + }) + if (suite.hasSuites()) { + each(suite.suites, function(suite) { + JSpec.runSuite(suite) + }) + } + hook('afterSuite', suite), suite.hook('after') + this.stats.suitesFinished++ + }, + + /** + * Report a failure for the current spec. + * + * @param {string} message + * @api public + */ + + fail : function(message) { + JSpec.currentSpec.fail(message) + }, + + /** + * Report a passing assertion for the current spec. + * + * @param {string} message + * @api public + */ + + pass : function(message) { + JSpec.currentSpec.pass(message) + }, + + /** + * Run a spec. + * + * @param {Spec} spec + * @api public + */ + + runSpec : function(spec) { + this.currentSpec = spec + try { this.evalBody(spec.body) } + catch (e) { fail(e) } + spec.runDeferredAssertions() + destub() + this.stats.specsFinished++ + this.stats.assertions += spec.assertions.length + }, + + /** + * Require a dependency, with optional message. + * + * @param {string} dependency + * @param {string} message (optional) + * @return {JSpec} + * @api public + */ + + requires : function(dependency, message) { + hook('requiring', dependency, message) + try { eval(dependency) } + catch (e) { throw 'JSpec depends on ' + dependency + ' ' + message } + return this + }, + + /** + * Query against the current query strings keys + * or the queryString specified. + * + * @param {string} key + * @param {string} queryString + * @return {string, null} + * @api private + */ + + query : function(key, queryString) { + var queryString = (queryString || (main.location ? main.location.search : null) || '').substring(1) + return inject(queryString.split('&'), null, function(value, pair){ + parts = pair.split('=') + return parts[0] == key ? parts[1].replace(/%20|\+/gmi, ' ') : value + }) + }, + + /** + * Throw a JSpec related error. + * + * @param {string} message + * @param {Exception} e + * @api public + */ + + error : function(message, e) { + throw (message ? message : '') + e.toString() + + (e.line ? ' near line ' + e.line : '') + }, + + /** + * Ad-hoc POST request for JSpec server usage. + * + * @param {string} uri + * @param {string} data + * @api private + */ + + post : function(uri, data) { + if (any(hook('posting', uri, data), haveStopped)) return + var request = this.xhr() + request.open('POST', uri, false) + request.setRequestHeader('Content-Type', 'application/json') + request.send(JSpec.JSON.encode(data)) + }, + + /** + * Instantiate an XMLHttpRequest. + * + * Here we utilize IE's lame ActiveXObjects first which + * allow IE access serve files via the file: protocol, otherwise + * we then default to XMLHttpRequest. + * + * @return {XMLHttpRequest, ActiveXObject} + * @api private + */ + + xhr : function() { + return this.ieXhr() || new JSpec.request + }, + + /** + * Return Microsoft piece of crap ActiveXObject. + * + * @return {ActiveXObject} + * @api public + */ + + ieXhr : function() { + function object(str) { + try { return new ActiveXObject(str) } catch(e) {} + } + return object('Msxml2.XMLHTTP.6.0') || + object('Msxml2.XMLHTTP.3.0') || + object('Msxml2.XMLHTTP') || + object('Microsoft.XMLHTTP') + }, + + /** + * Check for HTTP request support. + * + * @return {bool} + * @api private + */ + + hasXhr : function() { + return JSpec.request || 'ActiveXObject' in main + }, + + /** + * Try loading _file_ returning the contents + * string or null. Chain to locate / read a file. + * + * @param {string} file + * @return {string} + * @api public + */ + + tryLoading : function(file) { + try { return JSpec.load(file) } catch (e) {} + }, + + /** + * Load a _file_'s contents. + * + * @param {string} file + * @param {function} callback + * @return {string} + * @api public + */ + + load : function(file, callback) { + if (any(hook('loading', file), haveStopped)) return + if ('readFile' in main) + return readFile(file) + else if (this.hasXhr()) { + var request = this.xhr() + request.open('GET', file, false) + request.send(null) + if (request.readyState == 4 && + (request.status == 0 || + request.status.toString().charAt(0) == 2)) + return request.responseText + } + else + error("failed to load `" + file + "'") + }, + + /** + * Load, pre-process, and evaluate a file. + * + * @param {string} file + * @param {JSpec} + * @api public + */ + + exec : function(file) { + if (any(hook('executing', file), haveStopped)) return this + eval('with (JSpec){' + this.preprocess(this.load(file)) + '}') + return this + } + } + + // --- Node.js support + + if (typeof GLOBAL === 'object' && typeof exports === 'object') + quit = process.exit, + print = require('sys').puts, + readFile = require('fs').readFileSync + + // --- Utility functions + + var main = this, + find = JSpec.any, + utils = 'haveStopped stub hookImmutable hook destub map any last pass fail range each option inject select \ + error escape extend puts query strip color does addMatchers callIterator toArray equal'.split(/\s+/) + while (utils.length) eval('var ' + utils[0] + ' = JSpec.' + utils.shift()) + if (!main.setTimeout) main.setTimeout = function(callback){ callback() } + + // --- Matchers + + addMatchers({ + equal : "===", + eql : "equal(actual, expected)", + be : "alias equal", + be_greater_than : ">", + be_less_than : "<", + be_at_least : ">=", + be_at_most : "<=", + be_a : "actual.constructor == expected", + be_an : "alias be_a", + be_an_instance_of : "actual instanceof expected", + be_null : "actual == null", + be_true : "actual == true", + be_false : "actual == false", + be_undefined : "typeof actual == 'undefined'", + be_type : "typeof actual == expected", + match : "typeof actual == 'string' ? actual.match(expected) : false", + respond_to : "typeof actual[expected] == 'function'", + have_length : "actual.length == expected", + be_within : "actual >= expected[0] && actual <= last(expected)", + have_length_within : "actual.length >= expected[0] && actual.length <= last(expected)", + + receive : { defer : true, match : function(actual, method, times) { + proxy = new JSpec.ProxyAssertion(actual, method, times, this.negate) + JSpec.currentSpec.assertions.push(proxy) + return proxy + }}, + + be_empty : function(actual) { + if (actual.constructor == Object && actual.length == undefined) + for (var key in actual) + return false; + return !actual.length + }, + + include : function(actual) { + for (state = true, i = 1; i < arguments.length; i++) { + arg = arguments[i] + switch (actual.constructor) { + case String: + case Number: + case RegExp: + case Function: + state = actual.toString().indexOf(arg) !== -1 + break + + case Object: + state = arg in actual + break + + case Array: + state = any(actual, function(value){ return equal(value, arg) }) + break + } + if (!state) return false + } + return true + }, + + throw_error : { match : function(actual, expected, message) { + try { actual() } + catch (e) { + this.e = e + var assert = function(arg) { + switch (arg.constructor) { + case RegExp : return arg.test(e.message || e.toString()) + case String : return arg == (e.message || e.toString()) + case Function : return e instanceof arg || e.name == arg.name + } + } + return message ? assert(expected) && assert(message) : + expected ? assert(expected) : + true + } + }, message : function(actual, expected, negate) { + // TODO: refactor when actual is not in expected [0] + var message_for = function(i) { + if (expected[i] == undefined) return 'exception' + switch (expected[i].constructor) { + case RegExp : return 'exception matching ' + puts(expected[i]) + case String : return 'exception of ' + puts(expected[i]) + case Function : return expected[i].name || 'Error' + } + } + exception = message_for(1) + (expected[2] ? ' and ' + message_for(2) : '') + return 'expected ' + exception + (negate ? ' not ' : '' ) + + ' to be thrown, but ' + (this.e ? 'got ' + puts(this.e) : 'nothing was') + }}, + + have : function(actual, length, property) { + return actual[property].length == length + }, + + have_at_least : function(actual, length, property) { + return actual[property].length >= length + }, + + have_at_most :function(actual, length, property) { + return actual[property].length <= length + }, + + have_within : function(actual, range, property) { + length = actual[property].length + return length >= range.shift() && length <= range.pop() + }, + + have_prop : function(actual, property, value) { + return actual[property] == null || + actual[property] instanceof Function ? false: + value == null ? true: + does(actual[property], 'eql', value) + }, + + have_property : function(actual, property, value) { + return actual[property] == null || + actual[property] instanceof Function ? false: + value == null ? true: + value === actual[property] + } + }) + +})() diff --git a/frameworks/jspec/jspec.shell.js b/frameworks/jspec/jspec.shell.js new file mode 100644 index 0000000..cb19c69 --- /dev/null +++ b/frameworks/jspec/jspec.shell.js @@ -0,0 +1,39 @@ + +// JSpec - Shell - Copyright TJ Holowaychuk (MIT Licensed) + +;(function(){ + + var _quit = quit + + Shell = { + + // --- Global + + main: this, + + // --- Commands + + commands: { + quit: ['Terminate the shell', function(){ _quit() }], + exit: ['Terminate the shell', function(){ _quit() }], + p: ['Inspect an object', function(o){ return o.toSource() }] + }, + + /** + * Start the interactive shell. + * + * @api public + */ + + start : function() { + for (var name in this.commands) + if (this.commands.hasOwnProperty(name)) + this.commands[name][1].length ? + this.main[name] = this.commands[name][1] : + this.main.__defineGetter__(name, this.commands[name][1]) + } + } + + Shell.start() + +})() \ No newline at end of file diff --git a/frameworks/jspec/jspec.timers.js b/frameworks/jspec/jspec.timers.js new file mode 100644 index 0000000..c88d10b --- /dev/null +++ b/frameworks/jspec/jspec.timers.js @@ -0,0 +1,90 @@ + +// JSpec - Mock Timers - Copyright TJ Holowaychuk (MIT Licensed) + +;(function(){ + + /** + * Version. + */ + + mockTimersVersion = '1.0.2' + + /** + * Localized timer stack. + */ + + var timers = [] + + /** + * Set mock timeout with _callback_ and timeout of _ms_. + * + * @param {function} callback + * @param {int} ms + * @return {int} + * @api public + */ + + setTimeout = function(callback, ms) { + var id + return id = setInterval(function(){ + callback() + clearInterval(id) + }, ms) + } + + /** + * Set mock interval with _callback_ and interval of _ms_. + * + * @param {function} callback + * @param {int} ms + * @return {int} + * @api public + */ + + setInterval = function(callback, ms) { + callback.step = ms, callback.current = callback.last = 0 + return timers[timers.length] = callback, timers.length + } + + /** + * Destroy timer with _id_. + * + * @param {int} id + * @return {bool} + * @api public + */ + + clearInterval = clearTimeout = function(id) { + return delete timers[--id] + } + + /** + * Reset timers. + * + * @return {array} + * @api public + */ + + resetTimers = function() { + return timers = [] + } + + /** + * Increment each timers internal clock by _ms_. + * + * @param {int} ms + * @api public + */ + + tick = function(ms) { + for (var i = 0, len = timers.length; i < len; ++i) + if (timers[i] && (timers[i].current += ms)) + if (timers[i].current - timers[i].last >= timers[i].step) { + var times = Math.floor((timers[i].current - timers[i].last) / timers[i].step) + var remainder = (timers[i].current - timers[i].last) % timers[i].step + timers[i].last = timers[i].current - remainder + while (times-- && timers[i]) timers[i]() + } + } + +})() \ No newline at end of file diff --git a/frameworks/jspec/jspec.xhr.js b/frameworks/jspec/jspec.xhr.js new file mode 100644 index 0000000..6164879 --- /dev/null +++ b/frameworks/jspec/jspec.xhr.js @@ -0,0 +1,195 @@ + +// JSpec - XHR - Copyright TJ Holowaychuk (MIT Licensed) + +(function(){ + + var lastRequest + + // --- Original XMLHttpRequest + + var OriginalXMLHttpRequest = 'XMLHttpRequest' in this ? + XMLHttpRequest : + function(){} + var OriginalActiveXObject = 'ActiveXObject' in this ? + ActiveXObject : + undefined + + // --- MockXMLHttpRequest + + var MockXMLHttpRequest = function() { + this.requestHeaders = {} + } + + MockXMLHttpRequest.prototype = { + status: 0, + async: true, + readyState: 0, + responseText: '', + abort: function(){}, + onreadystatechange: function(){}, + + /** + * Return response headers hash. + */ + + getAllResponseHeaders : function(){ + return this.responseHeaders + }, + + /** + * Return case-insensitive value for header _name_. + */ + + getResponseHeader : function(name) { + return this.responseHeaders[name.toLowerCase()] + }, + + /** + * Set case-insensitive _value_ for header _name_. + */ + + setRequestHeader : function(name, value) { + this.requestHeaders[name.toLowerCase()] = value + }, + + /** + * Open mock request. + */ + + open : function(method, url, async, user, password) { + this.user = user + this.password = password + this.url = url + this.readyState = 1 + this.method = method.toUpperCase() + if (async != undefined) this.async = async + if (this.async) this.onreadystatechange() + }, + + /** + * Send request _data_. + */ + + send : function(data) { + var self = this + this.data = data + this.readyState = 4 + if (this.method == 'HEAD') this.responseText = null + this.responseHeaders['content-length'] = (this.responseText || '').length + if(this.async) this.onreadystatechange() + lastRequest = function(){ + return self + } + } + } + + // --- Response status codes + + JSpec.statusCodes = { + 100: 'Continue', + 101: 'Switching Protocols', + 200: 'OK', + 201: 'Created', + 202: 'Accepted', + 203: 'Non-Authoritative Information', + 204: 'No Content', + 205: 'Reset Content', + 206: 'Partial Content', + 300: 'Multiple Choice', + 301: 'Moved Permanently', + 302: 'Found', + 303: 'See Other', + 304: 'Not Modified', + 305: 'Use Proxy', + 307: 'Temporary Redirect', + 400: 'Bad Request', + 401: 'Unauthorized', + 402: 'Payment Required', + 403: 'Forbidden', + 404: 'Not Found', + 405: 'Method Not Allowed', + 406: 'Not Acceptable', + 407: 'Proxy Authentication Required', + 408: 'Request Timeout', + 409: 'Conflict', + 410: 'Gone', + 411: 'Length Required', + 412: 'Precondition Failed', + 413: 'Request Entity Too Large', + 414: 'Request-URI Too Long', + 415: 'Unsupported Media Type', + 416: 'Requested Range Not Satisfiable', + 417: 'Expectation Failed', + 422: 'Unprocessable Entity', + 500: 'Internal Server Error', + 501: 'Not Implemented', + 502: 'Bad Gateway', + 503: 'Service Unavailable', + 504: 'Gateway Timeout', + 505: 'HTTP Version Not Supported' + } + + /** + * Mock XMLHttpRequest requests. + * + * mockRequest().and_return('some data', 'text/plain', 200, { 'X-SomeHeader' : 'somevalue' }) + * + * @return {hash} + * @api public + */ + + function mockRequest() { + return { and_return : function(body, type, status, headers) { + XMLHttpRequest = MockXMLHttpRequest + ActiveXObject = false + status = status || 200 + headers = headers || {} + headers['content-type'] = type + JSpec.extend(XMLHttpRequest.prototype, { + responseText: body, + responseHeaders: headers, + status: status, + statusText: JSpec.statusCodes[status] + }) + }} + } + + /** + * Unmock XMLHttpRequest requests. + * + * @api public + */ + + function unmockRequest() { + XMLHttpRequest = OriginalXMLHttpRequest + ActiveXObject = OriginalActiveXObject + } + + JSpec.include({ + name: 'Mock XHR', + + // --- Utilities + + utilities : { + mockRequest: mockRequest, + unmockRequest: unmockRequest + }, + + // --- Hooks + + afterSpec : function() { + unmockRequest() + }, + + // --- DSLs + + DSLs : { + snake : { + mock_request: mockRequest, + unmock_request: unmockRequest, + last_request: function(){ return lastRequest() } + } + } + + }) +})() \ No newline at end of file diff --git a/frameworks/uki b/frameworks/uki new file mode 120000 index 0000000..2c86ea7 --- /dev/null +++ b/frameworks/uki @@ -0,0 +1 @@ +../../uki/src \ No newline at end of file diff --git a/i/button/down-c.gif b/i/button/down-c.gif new file mode 100644 index 0000000..a6b86c8 Binary files /dev/null and b/i/button/down-c.gif differ diff --git a/i/button/down-c.png b/i/button/down-c.png new file mode 100644 index 0000000..2c479ad Binary files /dev/null and b/i/button/down-c.png differ diff --git a/i/button/down-h.gif b/i/button/down-h.gif new file mode 100644 index 0000000..6dfe461 Binary files /dev/null and b/i/button/down-h.gif differ diff --git a/i/button/down-h.png b/i/button/down-h.png new file mode 100644 index 0000000..13a46a9 Binary files /dev/null and b/i/button/down-h.png differ diff --git a/i/button/down-m.png b/i/button/down-m.png new file mode 100644 index 0000000..8591606 Binary files /dev/null and b/i/button/down-m.png differ diff --git a/i/button/down-v.png b/i/button/down-v.png new file mode 100644 index 0000000..f50c2ac Binary files /dev/null and b/i/button/down-v.png differ diff --git a/i/button/focusRing-c.png b/i/button/focusRing-c.png new file mode 100644 index 0000000..60fd586 Binary files /dev/null and b/i/button/focusRing-c.png differ diff --git a/i/button/focusRing-h.png b/i/button/focusRing-h.png new file mode 100644 index 0000000..dc67c3a Binary files /dev/null and b/i/button/focusRing-h.png differ diff --git a/i/button/focusRing-m.png b/i/button/focusRing-m.png new file mode 100644 index 0000000..42def0a Binary files /dev/null and b/i/button/focusRing-m.png differ diff --git a/i/button/focusRing-v.png b/i/button/focusRing-v.png new file mode 100644 index 0000000..a08fa95 Binary files /dev/null and b/i/button/focusRing-v.png differ diff --git a/i/button/focusRing.png b/i/button/focusRing.png new file mode 100644 index 0000000..4b65e09 Binary files /dev/null and b/i/button/focusRing.png differ diff --git a/i/button/hover-c.gif b/i/button/hover-c.gif new file mode 100644 index 0000000..19cb30b Binary files /dev/null and b/i/button/hover-c.gif differ diff --git a/i/button/hover-c.png b/i/button/hover-c.png new file mode 100644 index 0000000..e408798 Binary files /dev/null and b/i/button/hover-c.png differ diff --git a/i/button/hover-h.gif b/i/button/hover-h.gif new file mode 100644 index 0000000..44cbfca Binary files /dev/null and b/i/button/hover-h.gif differ diff --git a/i/button/hover-h.png b/i/button/hover-h.png new file mode 100644 index 0000000..acef50e Binary files /dev/null and b/i/button/hover-h.png differ diff --git a/i/button/hover-m.png b/i/button/hover-m.png new file mode 100644 index 0000000..49ed6c4 Binary files /dev/null and b/i/button/hover-m.png differ diff --git a/i/button/hover-v.png b/i/button/hover-v.png new file mode 100644 index 0000000..7d944dc Binary files /dev/null and b/i/button/hover-v.png differ diff --git a/i/button/hover.png b/i/button/hover.png new file mode 100644 index 0000000..fdad6d1 Binary files /dev/null and b/i/button/hover.png differ diff --git a/i/button/normal-c.gif b/i/button/normal-c.gif new file mode 100644 index 0000000..dee658b Binary files /dev/null and b/i/button/normal-c.gif differ diff --git a/i/button/normal-c.png b/i/button/normal-c.png new file mode 100644 index 0000000..1845d59 Binary files /dev/null and b/i/button/normal-c.png differ diff --git a/i/button/normal-h.gif b/i/button/normal-h.gif new file mode 100644 index 0000000..783a7a3 Binary files /dev/null and b/i/button/normal-h.gif differ diff --git a/i/button/normal-h.png b/i/button/normal-h.png new file mode 100644 index 0000000..d913f1d Binary files /dev/null and b/i/button/normal-h.png differ diff --git a/i/button/normal-m.png b/i/button/normal-m.png new file mode 100644 index 0000000..ca15ec5 Binary files /dev/null and b/i/button/normal-m.png differ diff --git a/i/button/normal-v.png b/i/button/normal-v.png new file mode 100644 index 0000000..dd160ae Binary files /dev/null and b/i/button/normal-v.png differ diff --git a/i/button/normal.png b/i/button/normal.png new file mode 100644 index 0000000..8ae97bb Binary files /dev/null and b/i/button/normal.png differ diff --git a/i/checkbox/checkbox.png b/i/checkbox/checkbox.png new file mode 100644 index 0000000..9cab45f Binary files /dev/null and b/i/checkbox/checkbox.png differ diff --git a/i/checkbox/focus.png b/i/checkbox/focus.png new file mode 100644 index 0000000..eeae08b Binary files /dev/null and b/i/checkbox/focus.png differ diff --git a/i/checkbox/normal.gif b/i/checkbox/normal.gif new file mode 100644 index 0000000..fbba8fb Binary files /dev/null and b/i/checkbox/normal.gif differ diff --git a/i/checkbox/normal.png b/i/checkbox/normal.png new file mode 100644 index 0000000..bd40deb Binary files /dev/null and b/i/checkbox/normal.png differ diff --git a/i/panel/dark-h.gif b/i/panel/dark-h.gif new file mode 100644 index 0000000..2adc600 Binary files /dev/null and b/i/panel/dark-h.gif differ diff --git a/i/panel/dark-h.png b/i/panel/dark-h.png new file mode 100644 index 0000000..6439592 Binary files /dev/null and b/i/panel/dark-h.png differ diff --git a/i/panel/dark-m.png b/i/panel/dark-m.png new file mode 100644 index 0000000..ebb2a08 Binary files /dev/null and b/i/panel/dark-m.png differ diff --git a/i/panel/dark.png b/i/panel/dark.png new file mode 100644 index 0000000..e9023a2 Binary files /dev/null and b/i/panel/dark.png differ diff --git a/i/popup/normal.png b/i/popup/normal.png new file mode 100644 index 0000000..cda2328 Binary files /dev/null and b/i/popup/normal.png differ diff --git a/i/radio/focus.png b/i/radio/focus.png new file mode 100644 index 0000000..9443f3f Binary files /dev/null and b/i/radio/focus.png differ diff --git a/i/radio/normal.gif b/i/radio/normal.gif new file mode 100644 index 0000000..22f44c3 Binary files /dev/null and b/i/radio/normal.gif differ diff --git a/i/radio/normal.png b/i/radio/normal.png new file mode 100644 index 0000000..6e6a9cf Binary files /dev/null and b/i/radio/normal.png differ diff --git a/i/radio/radio.png b/i/radio/radio.png new file mode 100644 index 0000000..6cb0714 Binary files /dev/null and b/i/radio/radio.png differ diff --git a/i/shadow/large-c.png b/i/shadow/large-c.png new file mode 100644 index 0000000..444e613 Binary files /dev/null and b/i/shadow/large-c.png differ diff --git a/i/shadow/large-h.png b/i/shadow/large-h.png new file mode 100644 index 0000000..059f77c Binary files /dev/null and b/i/shadow/large-h.png differ diff --git a/i/shadow/large-m.png b/i/shadow/large-m.png new file mode 100644 index 0000000..08f8069 Binary files /dev/null and b/i/shadow/large-m.png differ diff --git a/i/shadow/large-v.png b/i/shadow/large-v.png new file mode 100644 index 0000000..0efed34 Binary files /dev/null and b/i/shadow/large-v.png differ diff --git a/i/shadow/large.png b/i/shadow/large.png new file mode 100644 index 0000000..3d56219 Binary files /dev/null and b/i/shadow/large.png differ diff --git a/i/slider/bar-m.gif b/i/slider/bar-m.gif new file mode 100644 index 0000000..014bb8d Binary files /dev/null and b/i/slider/bar-m.gif differ diff --git a/i/slider/bar-m.png b/i/slider/bar-m.png new file mode 100644 index 0000000..7896394 Binary files /dev/null and b/i/slider/bar-m.png differ diff --git a/i/slider/bar-v.gif b/i/slider/bar-v.gif new file mode 100644 index 0000000..c4ef246 Binary files /dev/null and b/i/slider/bar-v.gif differ diff --git a/i/slider/bar-v.png b/i/slider/bar-v.png new file mode 100644 index 0000000..dc1b50a Binary files /dev/null and b/i/slider/bar-v.png differ diff --git a/i/slider/bar.png b/i/slider/bar.png new file mode 100644 index 0000000..adaeb82 Binary files /dev/null and b/i/slider/bar.png differ diff --git a/i/slider/focus.png b/i/slider/focus.png new file mode 100644 index 0000000..f916ebc Binary files /dev/null and b/i/slider/focus.png differ diff --git a/i/slider/handle.gif b/i/slider/handle.gif new file mode 100644 index 0000000..f7f9096 Binary files /dev/null and b/i/slider/handle.gif differ diff --git a/i/splitPane/horizontal.gif b/i/splitPane/horizontal.gif new file mode 100644 index 0000000..96b3795 Binary files /dev/null and b/i/splitPane/horizontal.gif differ diff --git a/i/splitPane/horizontal.png b/i/splitPane/horizontal.png new file mode 100644 index 0000000..4561d48 Binary files /dev/null and b/i/splitPane/horizontal.png differ diff --git a/i/splitPane/vertical.gif b/i/splitPane/vertical.gif new file mode 100644 index 0000000..4856d37 Binary files /dev/null and b/i/splitPane/vertical.gif differ diff --git a/i/x.gif b/i/x.gif new file mode 100644 index 0000000..1e52e19 Binary files /dev/null and b/i/x.gif differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..72b479f --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + Uki_mail_app + + + + + + \ No newline at end of file diff --git a/server.rb b/server.rb new file mode 100644 index 0000000..cd3f709 --- /dev/null +++ b/server.rb @@ -0,0 +1,8 @@ +require 'json' +require 'csv' + +get '/messages/' do + response.header['Content-type'] = 'application/x-javascript; charset=UTF-8' + content = CSV.parse(File.read('tmp/messages.csv'), '|').to_json + "#{params['callback']}(#{content})" +end \ No newline at end of file diff --git a/spec.html b/spec.html new file mode 100644 index 0000000..269b7b7 --- /dev/null +++ b/spec.html @@ -0,0 +1,23 @@ + + + + + + + + + + +

JSpec

+
+
+ + \ No newline at end of file diff --git a/spec/spec.js b/spec/spec.js new file mode 100644 index 0000000..b96eef5 --- /dev/null +++ b/spec/spec.js @@ -0,0 +1,6 @@ + +describe 'uki_mail_app' + it 'should do something' + true.should.be true + end +end \ No newline at end of file diff --git a/uki_mail_app.js b/uki_mail_app.js new file mode 100644 index 0000000..e993df9 --- /dev/null +++ b/uki_mail_app.js @@ -0,0 +1,62 @@ +(function() { +// define namespace +uki_mail_app = {}; + +// all core modules +include('frameworks/uki/uki-core.js'); + +// used views, comment out unused ones +include('frameworks/uki/uki-view/view/box.js'); +include('frameworks/uki/uki-view/view/image.js'); +include('frameworks/uki/uki-view/view/button.js'); +// include('frameworks/uki/uki-view/view/checkbox.js'); +// include('frameworks/uki/uki-view/view/radio.js'); +include('frameworks/uki/uki-view/view/textField.js'); +include('frameworks/uki/uki-view/view/label.js'); +include('frameworks/uki/uki-view/view/list.js'); +include('frameworks/uki/uki-view/view/table.js'); +// include('frameworks/uki/uki-view/view/slider.js'); +include('frameworks/uki/uki-view/view/splitPane.js'); +include('frameworks/uki/uki-view/view/scrollPane.js'); +// include('frameworks/uki/uki-view/view/popup.js'); +include('frameworks/uki/uki-view/view/flow.js'); +// include('frameworks/uki/uki-view/view/toolbar.js'); + +// theme +include('uki_mail_app/theme.js'); + +// data +include('frameworks/uki/uki-data/ajax.js'); +include('frameworks/uki/uki-data/model.js'); + +include('uki_mail_app/view/toolbarButton.js'); +include('uki_mail_app/view/messageTable.js'); +include('uki_mail_app/view/searchField.js'); +include('uki_mail_app/view/messageTable/column.js'); +include('uki_mail_app/view/main.js'); +include('uki_mail_app/view/messageTable/dateColumn.js'); +include('uki_mail_app/view/toolbar.js'); +include('uki_mail_app/model/message.js'); +include('uki_mail_app/model/messageList.js'); +include('uki_mail_app/view/folders.js'); +include('uki_mail_app/view/folders/render.js'); +include('uki_mail_app/view/messageTable/drag.js'); + + +uki_mail_app.theme.imagePath = 'i/'; + +// skip interface creation if we're testing +if (window.TESTING) return; + +uki( + { view: 'uki_mail_app.view.Main', rect: '1000 1000', anchors: 'left top right bottom' } +).attachTo(window, '1000 1000'); + +if (window.messages) { + uki('MessageTable').data(window.messages); +} else { + uki.ajax({ url: '/messages/?callback=?', dataType: 'jsonp', success: function(data) { + uki('MessageTable').data(data); + } }) +} +})(); diff --git a/uki_mail_app/model.js b/uki_mail_app/model.js new file mode 100644 index 0000000..964f754 --- /dev/null +++ b/uki_mail_app/model.js @@ -0,0 +1,3 @@ +include('../uki_mail_app.js'); + +uki_mail_app.model = {}; \ No newline at end of file diff --git a/uki_mail_app/model/message.js b/uki_mail_app/model/message.js new file mode 100644 index 0000000..b25592a --- /dev/null +++ b/uki_mail_app/model/message.js @@ -0,0 +1,27 @@ +include('../model.js'); + +uki_mail_app.model.Message = uki.newClass(uki.data.Model, function(Base) { + + uki.data.model.addFields(this, ['id', 'subject', 'from', 'to', 'deliveryDate', 'body', 'unread', 'flagged']); + + this.loadBody = function(callback) { + if (this.body()) { + callback(this.body()); + } else { + this.bind('bodyLoaded', callback); + if (!this._loadingBody) { + this._loadingBody = true; + uki.ajax({ + url: '/message', + data: { id: this.id() }, + success: uki.proxy(function(body) { + this._body = body; + this.trigger('bodyLoaded', body); + this.unbind('bodyLoaded'); + }, this) + }); + } + } + }; + +}); \ No newline at end of file diff --git a/uki_mail_app/theme.js b/uki_mail_app/theme.js new file mode 100644 index 0000000..676e3ff --- /dev/null +++ b/uki_mail_app/theme.js @@ -0,0 +1,378 @@ +(function() { + var defaultCss = 'position:absolute;z-index:100;-moz-user-focus:none;font-family:Arial,Helvetica,sans-serif;'; + + uki_mail_app.theme = uki.extend({}, uki.theme.Base, { + imagePath: '/src/uki-theme/airport/i/', + + backgrounds: { + // toolbar button + 'toolbar-button-full-normal': function() { + return new uki.background.Sliced9({ + v: [u("button-full/normal-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAACdElEQVQ4T52UWU/bQBhF5/8/tC9UKhUPrdRKFFHaLAQIjp0F7Gxk32wTmv6S2znjBFIWEfpwxTDfPQd7sG3q9bqei+8H+n58ov2DL3q/t+/Cmj1mL3GmVqtpO77v69vhkd59+KTjXEGdm4GWq5ULa/aY0aH7mDfValWblC/K2vt4oOOfeSdIl7+1SG4VJ0sX1uwxo0MXZtthgiAQKZcv9fnroa7CluI0g/kZp3eKb9dhvTWjCwO78Tghl57Pl9S4bmo6j/+VPRE+SOnCwOJwQhal0rn8oKHJbKF5nGa3uC18kuwI6MLAOod1Gc+rqFA8V38w1myRPJxZ+krWZwoDiwOXycwNjadz9xcz4W6hCwOLA5cVXihsdu9vd5Fk0t2S3t82DlyGS+32hm5ztkhdKO2STR8WBy6Ty5+pZ8+Ay57MYvufS94UGFgcuMyPk4Lq1y1F7a6a7Z6iztviGMviwGV+2Weobp8lBq3u4L8CiwOXOTsrK6iHbtDpjdTpj9TtD3cKXRhYHLiMV/FV9qpucDOYZBnumHUfFgcuU6lUVCyV7eZQ/dFMg/Fcg8mOsV0YWBy47HNYUrXWUFALNbSl8TTWaJZo/Epcx3ZhYHHgMqenp87s2Utu2cOdzFNNY/tKvRI6dGFgceByQhKFkS69wJ3HIrnTPLXfwhfiZrZDFwZ24zHFYlGEX1qttv1y1BW1biywVLJcKbn7o3Qd1uwxo0MXBnbjMYVCQZuwEUWRfS/bCqrX9nb67pzi25ULa/aY0aELs+0w+Xxej8N3rdu1b46FGldNe+BXLqzZY5Z9lJ+yJpfL6bkw9DxPYRg6AWHNHrOXuL9Q4QqDB1Un4AAAAABJRU5ErkJggg=="], + m: [u("button-full/normal-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAXCAYAAADtNKTnAAAAiUlEQVQYGa3BMU4DURBEwdczHREgDu7ECRdcgYitdchv+DdgNVTpdn8Pv1bCVhJX6ePzKxAksSUBxN8EED6fT6YsianiHzhhzBCmTMKUw5yTMOW1wpQf58mUS8WUu8WUVc2Uu5spdzVT7i6mXNVMubuYcqmY8nEcbOt7sVUXV/nt9YUtCZskrvoBSb0pdiud7ZQAAAAASUVORK5CYII="] + }, "0 10 0 10"); + }, + + 'toolbar-button-full-hover': function() { + return new uki.background.Sliced9({ + v: [u("button-full/hover-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAACZElEQVQ4T52U227aQBRF5/+l9imVEqlV1aqtgBBc4wvYHl+xPeAY0iRVfmf37CGoFEFFeNhimLPXshMPVlVV4ViyLMdwPMH1py94f3VjwzX3ODvFqbIssZ8sy/B9MMK7DzcYOh7qtsPzy4sN19zjjB12D3lVFAV2iaMYV9cfMbhz8fT8gv7hEcv7NcxruOYeZ+ywS2bfofI8BxPHCT5/GyDJF1j1DzBd/yrbyOc22/XazthhlwzZnccKeeuTnwHmukBtOrSrXqDtHR0LZ+ywS4YsHVbIhR/MEMxTLOoVmmVvy1vpVvxv/s7ZJUOWDrqU1ikcN0RWNqiaTq66LZ4TdsmQpYMuRbM/S1AsjB0u2vs3hQxZOuhSrjfHPCmQVy1Kuf2qeVvIkKWDLjVxAsSpnL+ylYFBLlcrzgy7ZMjSQZcaTzxE8qR0VkPnDXTRIj0z7FpGWDroUl9/jDAYuxjeTTGSjUtClg661GDk4FY2b+UsjS8MWTroUs40tBuO/EMdP7oswlqHuFQUa/niw5XH7s3Si0KWDrqUTlNrD6IMs6TETJfyczov7JIhSwddKgxDpHmJaZggzhoku6d3RtglQ5YOulQQBEjFHEQponSBggdVTn9Z/z/ssEuGrHWIywoZ0xqEcuu6NKiXa8kG9epEOJMOu2TI7jzK930w/LJadXLiK6RSbLtfMP0TlgfhHmfssEuG7M6jPM/DLtwwxqCRV30sp7+QP810j+g2v2245h5n7LBLZt+hptMpDsP3Wt/Lu1CuXi6W8jOrbbjmHmfsHGOV67o4Fg611mjb1goYrrnH2SnuDyOpxMgv4mmjAAAAAElFTkSuQmCC"], + m: [u("button-full/hover-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAXCAYAAADtNKTnAAAAmUlEQVQ4y82UsQrDMAxEJUteOhX621my9NdK6Tek4HSOr0PqEmgSCrohNxlxPE7CnHb9FfJRxfxMqrKlNY/e7o8vpM2BTcaqx8v4kqiSEOQiiEMAQpJacRAIUBlJCJBpIqwzPIc4JKX4f3Nzj0PcCJCcGUk8E5IwbmL5KIc1szhkLGXuzEVx6k5RoxWQqjSXX86nH8M/kKXnDbaGN3Y1p/buAAAAAElFTkSuQmCC"] + }, "0 10 0 10"); + }, + + 'toolbar-button-full-down': function() { + return new uki.background.Sliced9({ + v: [u("button-full/pressed-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAACd0lEQVQ4T52Ua1PaUBRF779vHbVT1IJj0IIMjwTI8yaSB+GhgljqtOMP2j37tjjWQSv9sGau5+y9TJxr1Hw+xzbKcgJ7METtvIGDk1MDz5xx91pPzWYzPKcsS7R7Dg6PTzEING7u7vHw49HAM2fcMcPsy76aTqfYkKUZjk/rcNwQDz8fUUxvEKdjofzD2My4Y4ZZdp471GQyAUmzDOetDkaFnMsZ9Oi3KMn+hjPumGGWHXY3HiMcj8dodfvoDQMEcYboqngSboM7Zphlh106jJB/h4EXouP48KKRBPMn4Vswwyw77NJBl8rzAo1OH31fw9fydEn+JH2b3GTZYZcOupTrh2j1hhj4iSxT8xok/AebHDvs0kGX6vZdtB1PhjHccGRewYvSdzIyHXbpoEs12w7atg/H0/Losfltu8AOu3TQpaxGRx7XFXsIexj9F+zSQZfal1v/4XMVHytV7FVq2DvaEemwSwdd6sRqYP/kDAdVC4eG+o5YpksHXarRdfDp7CsqVhOVehNH9cudYIddOuhSgU5wfNHCF3n/arOLGrnsvQ/JssMuHXSZi33W6sHqDGDJXarb7k6wwy4d5mInSYI4K3DRddEcarQ8Er8TbTrs0kGXiuMYhfxj24FcTrlXPZ3DjoWkeBvJMMsOu3TQZYTkdrGEHaboS3iYz+GS4hVkxwyz7LC78SitNQh/WK5W8OXz5KZTRJMFotnddmTHDLPssLvxqCiKsIGDxXKJyc0SXjZDKEU9X+Hqdm3gmTPumGGWnecOFYYhXsLv2rf1d1wvV8iu5UnLawPPnHHHzLauCgL5Sm+By6IosFgssF6vDTxzxt1rvV9j938ICnFl4wAAAABJRU5ErkJggg=="], + m: [u("button-full/pressed-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAXCAYAAADtNKTnAAAAp0lEQVQ4y83UwQoCIRAG4P8XdS6deoDet2OHniuIHqBDUfQCUaDToVmWZQl2VyEHVBD8RkWH291eAcA5BwBQ1cE4JXg4nhRqiwjw243jl0nA3+4Py2xzxOzwXVpSe3ouMj671kDwLyTnciTlCjtJKTWCnK+XcsT7UI6EGFtBpAIiUcqRKNLKnYQaiL0TtTrCJUXp/XraYivQS5DNegUA6P4QyUGbUvk/4aM9dYCyVGwAAAAASUVORK5CYII="] + }, "0 10 0 10"); + }, + + // left + 'toolbar-button-left-normal': function() { + return new uki.background.Sliced9({ + v: [u("button-left/normal-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAXCAYAAADduLXGAAABwElEQVQ4y3XTyY4TMRDG8X9VO8t0OlFWFgkhEGeG5SG4cOCd8xhckDjMwASSzKST6bjtLg4dyEpJvlg/l0r6ym46nRoXKoTIbL5guVyxKQrKEHBmxzbGyO3dL37+XvLyxXM+fnjHcNTncb05xn7r+frtO8+ePuHL509U0YgxEnwkP8RlGbiZzXl//ZbJZEzpAyKCiILUzRyAmVEUJW9ev2Iw6GNmiAj/1K5cDT1Zt8tg0EdVkROEgRm4qjJixR6KHKvDzt6XdNIOruEOoJ06zAwHQju9OoJ2OsZfHCuj2WzsLuojYpdxVRmqiplRVRUigl3I1MxwIQTy9YZGI0FQTLhY+WaNS1wCGIIiqvzHoiK4VsMRY4W0FNU6LeF8DlXFNZqO0ns0y2rMWXB7LBjee8D2oVzCSYLbbrdknS75as1wNEBFMTkLnES13o0YPGYJxeaRTreL6jlWTeqtK4qC4WDIw8M96hzdLDvrniS63+f5Ys54PGF1vySUZb1YSQK7R6IH2MyYze4YjUaYRW5vfpCmKWknpdlq1WMcfqv6wYwsy+j1eoRQslwsiDEQYjz/sACr1Yo8z+vOacpVu41zjj8Dq+vM/h7p/wAAAABJRU5ErkJggg=="], + m: [u("button-left/normal-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAXCAYAAAAV1F8QAAAAaElEQVRIx93Uuw2AMAwAUf8qCsRsSBTsREPDgt4AOW3MFlfgBZ5OiR3X/bQAE+exCwLVGAykqgwk0ES3UEUNQVBSNPdGUNGcEPRWMZCpMZA7dBnUnCqiIK4I+gz2vyJqjzKTgbZ1QaAPxYQYzEAnGikAAAAASUVORK5CYII="] + }, "0 1 0 10"); + }, + + 'toolbar-button-left-hover': function() { + return new uki.background.Sliced9({ + v: [u("button-left/hover-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAXCAYAAADduLXGAAABx0lEQVQ4jXWTzU7bQBRGz525/olxgSYgUdQ9QuortLu+OI/RTaVKVSvSxgQb23HGc7sIkEDNt/JIx2fufJrRm5sbYyIhjCxXFdW6pu16hmFAzV6y4zjy6/YPv/9WfLy85NP1NSenJfV9/RIeNgPfvv/g7Pycr18+M4QtIQSah4679XoPb7eBn8sVV1dXHL8reehaRARDEMAMFMDMaNqBDxcXzPKMcRwREQCEncxgZ267DVmec5TPEHE8CXjEn6IxGttgnJQ5JsLheXff9rzQfjOQpAnO7Y02UWY00DEKWZo8QrY3vYpFQ8cw4txu+xgjyOGUB+YY0WgRMyOOEURAZBIeY0TbviPEiIg81zWVu6pCU03wzoFzk8anOOfQLM8I0VDvd2O8Ee8VLfKU+7bHqcfh3oRVPeq9AII6j3P+zTa8V3TYbJiflDx0A0mSIW66DU308dbFgPcOs0iqKSaCswkzQN/3zN/PqeqOoEqepbyeR9Xv7/OqWnF2ds666dkAs9kMOajTe93DZsZyectisSCap2ka0jQlSzO8erz3L5/V7oclZVkyPz5mO0aGTUdoAyFs/3+wAHVd0zQNRVFQFAVHs5zFack/i6zxYmg7DrMAAAAASUVORK5CYII="], + m: [u("button-left/hover-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAXCAYAAAAV1F8QAAAAkElEQVRIx92WPQqAMAyFkyZdnASv7eLi1UQ8g0J1ts/B3wOUN/hNhUI/XtKWaNv1kIuMcxlUpSQQER3G6RHd5wNSHE/rJgyCkPCzggQRQEqUM34mAjIrEUm076TSzcvMEYXAebNu7hyRG0kUIyuRR1IiVo8s/u0ymBlHtKb0DBD3sKCFhpPv5+ZNXb0bhUVfDj/5LnR1f9QdAAAAAElFTkSuQmCC"] + }, "0 1 0 10"); + }, + + 'toolbar-button-left-down': function() { + return new uki.background.Sliced9({ + v: [u("button-left/pressed-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAXCAYAAADduLXGAAAB2ElEQVQ4y3WTy24TQRBFT9V099hjBxELkgUhLIAV4guQkPj1/AUrJBQR8Csej4Mzj+5i4TixHVNS7U7fut11W66urowj1XWRyXzOvKy4W99jMeHM9tkYIzfjKb8nc95dXvD501uKXp/pbLoPN3XD9x8/OT8749vXL8xvS8qyoixXlOXiCW7aluvxlI8f3iOq/LoZIyKIyEbJDAeQUmI8KxkUA+q6RVUfoe1gMzbKt8s7ssyThxxVebS1a9HM0BgTq3WNDw4RBQMwzPY7kXDV3V8yVQQFjGSAgXDwoglc3XaoPoDJEDFAOKyYEq5pOpwLJDMsReQICJBixDVth2aO2KW9yz3baIy4RbWkWywQYaP6H361XOL6vR7rukF0a+A4rd7hhsOCxoxMFLbqR8r7gDs9GVKta1SzzXoBRI7AHlfkgdx7JHOgO1k4qBByXNs2vDl/xZ/ZkswHVI/7vg9hk408EwZFTmdC5vzGxcGEEMImdfX9PRevR1xPl6gK6sOD9x3PIaDboCxu51yev8RniqSEdw7vw077JzilxGwy4exFn9NBTte2WEqICM45nNuBtwemkwnWrLkYDTntZQSJdPUaVXn+YQGqqmK1WlEUBcOiYNTv40Yn/AMQAvxPIRAzswAAAABJRU5ErkJggg=="], + m: [u("button-left/pressed-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAXCAYAAAAV1F8QAAAAfklEQVRIx2PpnDj1PwMdAIuboz0DA8gqRiJUU+AklucvXjHQxUfEeYUKFv3//3/UIjIt+vePPhb9/UcnH/39+3eYWfTg6RP6WMTCwkofi1jZ2IabRex0soidjZ0+FrGxsw+3OGKll0X0yke/fn6nj0VyQjwoAoyMmFU7NeosABiuJsuhWi5rAAAAAElFTkSuQmCC"] + }, "0 1 0 10"); + }, + + // right + 'toolbar-button-right-normal': function() { + return new uki.background.Sliced9({ + v: [u("button-right/normal-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAXCAYAAADduLXGAAAB0klEQVQ4y21S227TQBScvdiO4xjstoB4QURIvECqfidPvPBPVVWpEmp5ByouTWhD4jjJ+rKcs05C6nitY1v2zOzsnCM+fPxkPa3R7/WQJDGeHaXQWqFrieubLzaM+rj/M8XX2+/4dvsTL44TvHx+AqUek8T5xaVNkxSWLv4plcDV1Wf8+n2Ht8NX8AN/B5bNXUAIibquUZgSo/fvcHY6wo/xPYwpYK11JVuuiCTcjzRN8Gb4GqtVA3aa7mkPKJBSOsIgjolg9pXtXm0I4j+hqkEWLSsToF0bEhO0pxH1I+fdgW1L2+52awi9fujM6e1J22v/k+97ZMU2YPbTtXaRSekwOssX0L7fCRZ8BFD2RYWyLKEln1pIdKOpan4poWheNG+hlDi0wEjbYCvKLqBUHFhI1WkBG43CGHg+g2l42tO1Ta45fEUZG4QB2VBso6XsRBloa2TzBQZRiHwxZxusLA/AlqJaEbAqDDWmaiJkIBO2xf4VpbNcLjGb/aVWBzRIKyeihUtDNs3lrasKDw9TLCn/J3GEyWS867Czwd0x6zX5ypHnOYKeh5BqPL57NAp6Np0in89JXcPTEvEgoO1nyLLsICB9cvTUtZLjYQArdw0Wr3/Z9/0CcqEqewAAAABJRU5ErkJggg=="], + m: [u("button-right/normal-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAXCAYAAAAV1F8QAAAAhElEQVQYGb3BsQEBARBE0b+7E0noTSBQkkSiQFcCJzUCAQUw7+l0vth8dBX/oONhzzfbvBW/YaDQfX2QoKoiQYTIJkJgEoRNgkyGbJOg59Mk6LauJKirSdBMkaDqIUEzQ4KmhwTNNAnqHhI00ySoq0nQcl2wDQYauhqKn9Nuu+FbVfEPL+gFIdGp1E0bAAAAAElFTkSuQmCC"] + }, "0 10 0 1"); + }, + + 'toolbar-button-right-hover': function() { + return new uki.background.Sliced9({ + v: [u("button-right/hover-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAXCAYAAADduLXGAAABx0lEQVQ4y32SzVJTQRBGz8z0/QkQkQQpKEvU8jl057u5cuM7WfoCLly4AqoQpCBckty5M90ubhJBErrqm02f/qa7Z9ynz1+sLEu2BjV7u0NejPYQCawL9/Xbdxs+G3Jz3XB6fs7J2RmH4z2ODvYJ4WGRv765obmbIUXg3ZtjPn54Tygqfvz8RTtvMbOVxAwcDgNSSnRdx9vXx4xHI05OT3l5MKYopHc2AGyBAzhyzgzqiqPDQ5ppxKzPeQCzpWyVcM6zXQ+o6prprG/HY4bqUvcLwZxjMKjpUp8XNVA1NoX3nqIsmLcRMTXMdC24bE2koJ1nRFVRfQzb4jAD7x05ZSSrkrNtgHvazFBT5OrP1ZM9LzcUY4t47/Heb4YBVCmlQEIQgghPWJOAqq4QkYCEzbCikBNbddU7L99+3TZUMxFHCA6RQhAp1sKmRtdFRrs7tLO7hfN/PasDZ0bbRkLwoKn/GyKBUMg/iVCEQMpKTh2j4Rbz+Rygd14O2F+tzGYzNHWMn+9weXmx+okSQsB7T06ZNrbEGNmuS3wJFxe/VyCApNRxO7lGglCVnp26ZjKZ0DTNo6Hl1dE+KSVijNzdNkyn0wdu9+Mv25oPTSfSJYAAAAAASUVORK5CYII="], + m: [u("button-right/hover-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAXCAYAAAAV1F8QAAAAjElEQVQYGb3BMYoCURRE0Xr9708mElyTILg1k0lmlwqtsa9ExVlBU+dw/v2zNta2XpYqfXE6HrS1Kr3Z+sd6uysBhSBZCdiKoNtKoNtKwG4l0N1K4PGwErhcL0pgWRYlMEAJMFACc6IEYCqBCUpgTJQAAyUwxlACt3XV1mzrrUqlD/a7H23Ntl6qSl9PqVQpzP9kSucAAAAASUVORK5CYII="] + }, "0 10 0 1"); + }, + + 'toolbar-button-right-down': function() { + return new uki.background.Sliced9({ + v: [u("button-right/pressed-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAXCAYAAADduLXGAAAB0klEQVQ4T32SvW5TQRCFv/137BgnjkMA0QGRkJB4ACQkno+KhteiSgEFDQgTx4njOHZyd+/uUNgO18HhSCNt8c2ZM6tRHz99FmU0nZ0W/V6Xw34faw3bZN++ec3gYMDi9obfpyO+nHzlyWGfp48HGLPZpMfnE4anZ0ynM/Z6e3x4/w7nW5x8+051WyEid6URQSlQSlFVkV/DEb29Hq9evuDHaEwV419YBNa1VqwSVZXotDuMzqeUUpYxmmOk0aG1IviAMY7J1XzpXCj82yAgoJTGecv1TUXOBUsBWY1ZSxovhcZozWy+wOZSyKURuAEuUwlaa6pUY0vO5Jy3wCCsYokipoSt/wMDlCIUKcRUY38Oh1zO59tJWbuDNRqrncVatx1GEECKsBM81jmP8/5hVCBLYXe3jXXObYdl5SqCLpn97i7W+4ALDziLQBEk17SDxzrvCT5sQykFcowcHQ1IKWK99/hwD5blD+Q60WoHglFUSZbOG5mXO1FSRGnD88EjLicXAKsFXQMuhRQjzmiODrpcjMd312itdVhryTlTcianxH4nEAycn51tnK3VWlFXNwRn2GkZWt3A7GrK+Pqa+7LHzwbUdU2MkcV8xmSx2HBr6g9L/hlPJucDyAAAAABJRU5ErkJggg=="], + m: [u("button-right/pressed-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAXCAYAAAAV1F8QAAAAmklEQVRIx2Psnjz9PwMS+P//P1Y2pYDx3IXLENMYoQIIBn5AihuAZrE8f/kaVYyRgSaABdNg2tjEghkN/2llEW0MHkCL/v2jj0V//9HJR3///h1mFj14+oQ+FrGwsNLHIlY2tuFmETudLGJnY6ePRWzs7MMtjljZ6JWPWNCqPRpVfH9+fker9mhkkRQ/F1qbgRFOI7NxtZSIBQDHcS98n02qWAAAAABJRU5ErkJggg=="] + }, "0 10 0 1"); + }, + + // mid + 'toolbar-button-mid-normal': function() { + return new uki.background.Sliced9({ + v: [u("button-mid/normal-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAAXCAYAAAAhrZ4MAAAATUlEQVQIW2PpmjD1PwMQsHi5OjKAGV++foMwGKAAwfj/H1PkP0HGl29fIQwmRkYog4kJncHMDGEwI6RgIsxQEUYMqU8fPkAYIkL8YAYAr+gazL6h6nwAAAAASUVORK5CYII="], + m: [u("button-mid/normal-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAXCAYAAACBMvbiAAAAiElEQVQYGc3BoVIEARBDwZeZKATFf2Mw/OAJQMNiN1RhEJzeSreeX14T/huJq+nt/SPckYQ/4gr+Or5pYUm0GIo4oYYhtDAJLRx6OAktfJ6hhT+PgxYeDS28K1pYs7Tw7tLCO0sL7w4tPLO08O7QwqOhhW+3GznDrwACBKMBcSk/PT5wjySu9gMKwyN3ltbnfAAAAABJRU5ErkJggg=="] + }, "0 1 0 1"); + }, + + 'toolbar-button-mid-hover': function() { + return new uki.background.Sliced9({ + v: [u("button-mid/hover-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAAXCAYAAAAhrZ4MAAAAW0lEQVQIW2Pp6Jv8nwEIWOyszBjAjA8fP0IY/8ESIAYDlMEAZ0DlWP7B1fyDifz7B2H8hTHevX0HYTAxMUEYzMwQo1hYWJjRRVhZCKphZoaK/PnzG8KQlRQBMwDxCRzMOh3CMQAAAABJRU5ErkJggg=="], + m: [u("button-mid/hover-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAXCAYAAACBMvbiAAAAdklEQVQYGc3BMQrCUBRFwUP++Y2V4JoEwa3Z2LjLCIl13rN0Cd4ZH89XE6AB77crKdz2DykkiNCksJsYVjUprGpS2F2ksKpI4XE0KVzfKylcloUUDiWFDknhnJJCnaRwKikcU1LokBSOMUjhvm38U/Pj5XwixRdnPSDKXAN+9AAAAABJRU5ErkJggg=="] + }, "0 1 0 1"); + }, + + 'toolbar-button-mid-down': function() { + return new uki.background.Sliced9({ + v: [u("button-mid/pressed-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAAXCAYAAAAhrZ4MAAAAY0lEQVQIW13KMQ6AMAhAUdoAXsDJA5iYmHhQJxdPaNJF4wUsVQs4yMLLhzAva4FncBoHqNiPUwGlKGy/sCIg9iN2ymJFclZcji0lRSRUELGDFMyNFWYv/CvfM6IhxqDou7biBpSbHMl9F6bdAAAAAElFTkSuQmCC"], + m: [u("button-mid/pressed-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAXCAYAAACBMvbiAAAApElEQVRIx2Ppnjz9PwMW8P//f7x8WgAWZ1sbBI8RmcmIIUYUoMDNLM9fvsYqwcjIQHfAgttS+ruGBXdS+D8QjqG/pUPEMf/+DR7H/P03iELm79+/o47B6pgHT58MHsewsLAOHsewsrGNOga7Y9gHkWPY2dgHj2PY2NlH0wx2x7AOIscAAY6m1QA0rv78/I6jaTUAjpHi58LRBmZEYaPzCfUmyAEAxG8szbIQ5CAAAAAASUVORK5CYII="] + }, "0 1 0 1"); + }, + + + // panel + 'popup-normal': function() { + return new uki.background.Multi( + new uki.background.CssBox('opacity:0.95;background:#ECEDEE;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;border:1px solid #CCC'), + uki.theme.background('shadow-medium') + ); + }, + + 'panel-dark': function() { + return new uki.background.Sliced9({ + h: [u("panel/dark-h.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAECAYAAABodtTSAAAAIklEQVQYGWN89OjRfwYKAeOFCxcoN2T58uWUGxIYGEixIQC2uQv/2FmjnwAAAABJRU5ErkJggg=="], + m: [u("panel/dark-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAABCCAYAAACxdz2jAAAAQklEQVRYw+3SQREAIAwDwT7q31olBRHwAGYjoHO5pmcmtZmuA+kkhQQJser85YRYTt5y4jt2YiecEItEHTtBcv2RBQSZeQAFfY4LAAAAAElFTkSuQmCC", false, true] + }, "2 0 2 0"); + }, + 'panel-light': function() { + return new uki.background.Sliced9({ + h: [u("panel/light-h.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAABCAYAAAAFKSQHAAAAD0lEQVQImWO4cuXKf1IxAKwwLUBaex9CAAAAAElFTkSuQmCC"], + m: [u("panel/light-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAWCAYAAAAb+hYkAAAAMElEQVQ4y2P8+/fvfwYSARMDGYDl////w00TeQHx79+/wRwQ3759o09AjGoa1YQKAFiCG/x9cnwFAAAAAElFTkSuQmCC", true] + }, "1 0 0 0"); + }, + + // text field + 'input': function() { + return new uki.background.CssBox( + 'background:white;border: 1px solid #999;border-top-color:#555;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.4);-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.4);box-shadow:0 1px 0 rgba(255, 255, 255, 0.4)', + { inset: '0 0 0 0' } + ); + }, + + 'search-input': function() { + return new uki.background.Sliced9({ + v: [u("search-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAWCAYAAADXYyzPAAADJ0lEQVRIx52WSUtrQRBG+286ElciggiuXAiiLkRdKThtVIjgAIrzPCMqKLgISQQRFRUUnHEevsepR4er75nkeqGg0131nb7V1XXjVlZW9D+bnZ1VZ2enGhoaVFNTo+rqatXW1qquri6t4UsMsWj8pO+Wl5cVtLm5ObW1tamxsVG9vb1aWFjQzs6O4vG4EomEkslkWsOXGGLRQAvN7xy3tLQkb2NjY2pqalJ/f792d3d1enqq29tbPT096eXlRa+vrxkNX2KIRQMtNNEOslLgiYkJNTc32/jk5EQPDw96f3/Xx8eHPj8/le2DLzHEooEWmmjDSIEXFxctFa2trVpfX9fl5aXtPAws3SbQQhNtGLBgGrirq0vz8/Pm8Pb2ZgHYxsaG2tvbVVFRYdbR0WFzYTOAJtowYBl4ampKfX19Oj4+/vKmIyMjikQiKigoUF5enhlj5lgLC0cbBiyYLhqNamtry87Di/FWhYWFKikpsR1ub2+bMWaONXzCph0GLJiOHRweHlo6eCiMlpYW5ebmqqenR9fX1xaAXV1d2Rxr+OAb5oEBC6abnJy0/HsRqrG0tFQ5OTl2L4NnTrqYYw2fsGD8YcF0q6urX9IMiHQifnBw8OUsGTPHGj5s8jfphulmZmb0/PycAiBGx0F8aGjoHzBzrOHjjycMGNb09PTfqn58fEwBSAebQbysrEyjo6M6Ozszo5qZY43537wxLKtq3oAWFwTzu7u724qICi4qKjJjzBzgyspK3dzchAajDdMNDg5aWwsWCimkgumv9fX1Ki4uNmM8MDCg8vJyu9dVVVUmFKa4YMF0iG9ubtpHIPiQRkTPz88tzTT9i4sLq8r9/X2D5+fnG/zu7i4rMAxYMB2Ne3h42ES/Xw/f7MkAxtifk4eTfppLNm8LAxZMA7MDmnfwWmU6Kz5/wPnYZ4rz14geDcvA4+PjwqhY0hCs8ExwUod/ukbiM0SLheF5dsbeuCJ8vjjbbLtSuk36G8JfHaBBlgMWNBxoaXt7e7q/v7ez9X8GsjF8iSEWDbTQ/M5xTP5knMXa2ppisZi1yqOjo4yGLzHEptP+A2XnG0kH7B78AAAAAElFTkSuQmCC"], + m: [u("search-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAWCAYAAAD5Jg1dAAAAMUlEQVQoke3KsREAMAgCwLi7Q9KyhgW4Am3u/Pqru/0CBSCLM5NF2xcv/hclZZFkFBfzW1Vt2gDiogAAAABJRU5ErkJggg=="] + }, "0 10 0 20", {inset: '0 -10 0 -18'}); + }, + 'search-input-focus': function() { + return new uki.background.Sliced9({ + v: [u("search/focus-v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAcCAYAAAAEN20fAAAEMUlEQVRIx7WV+09TdxyGz5+6DEQ3Zc4lu6EycaDsBltwihMo1FIKvdCettAWOL3RlrYC5eIER3DB7ILb4mKWbb+94znLMQy0g0i/ySf8cD7v87x8OeEY+Xxe/zfpbF7JdEHBmZLGkmV5EvWHHXbJkD2Kw8jlcnrZWJmcEta8xpMlDcSr6gkv6lpwWVf99YcddsmQhQGrnss4SoFPQou6NF5Tq2dNp9331TRcf9hhlwzZoxQystms9s9cOitzJi/3VEmfh+7pom9Zb7lrau4vqqlvVs29MTV/EdGpHlMtvdFDwzN27N29DFkYsGDCxnHQa2QyGTkza2UUSeU0GFtQV2BJ73hqarpVUEtfSm0uSzcm996RuZoihW9lFh8oWto4NDxjh10yZGHAggkbB679biOdTotxSgxES7oeqOq9uxWd6Uvo0khGo9a6CvcfyzjmIUMWBiyYsHE4ZRz/8yLxmaxcsaI6/Yt631PVua+Tuhkpa35959gFDh4YsGDCxoEL5/MilmVpZi4t79S8PgtW7Cts7Z/T7dg9be48eeUSzoEFEzYOXDhx08EuEktldNtcUNvYklq+WdB1f0XV73ZPrIRzYMLGgQsnbrtIatbSaDyv7r2/XevIsi4MFRSpfH/iJZwDGwcunLjpYJgJS/2Rkj70Lur0nbJ6zBWt7vzesCKwceDCiZsOxlg8q08DZZ11r+j8UEn+4nbDSjgHBy6cuOlgDJs5XR2v6tTwmi56q7LWf2p4ERy4cOKmgzEQzqp9rKrXXev2d2Jh67eGF8GBCyduOvynCD8r208bXgTHfqddZCicUYevombXqn1d8w8bfyM4cOHETQfDY1rqnijpzZGaLgyXNbWy2/AiOHDhxE0HIxRL6UYwb//rfWNw75oyj7Tx858NKwMbBy6cuOlgROPTGg5b6vSVdG54Se3+VWU2G/eewMaBCyduOhjxeFwBM6G+QM5ueNa190HK7zTkVmDCxoELJ2462EXM2JRck3O65ivobfeSLvsWlVj75cSLwGwfX7IduHDitovEYjEx/si0bgUsfeRd0LvusrpCq0qu/6rN3b9euRAMWDBh48CF0/Eb0WhUTNiMyTs5rb4JSx3egh3oCKxopPiD8lvP9PDJ38cuRIYsjCv+2r/MPTYOXDgdv2GappyZjEQ1GpqyF9tHSzo/smh/JbvMTblLP2r2wVOVHz3T8uM/6g477JIhCwMWTNg4cO13G5FIRPsnFDblCcZ1c2JGH4/m7d+ClwvYB74VdUc31JvY0lep7RcOz9hhlwxZGLBgwsZx0HuoiFPGF4ppIJDSl+NpXRmdt2FnXEtqGlrRa4OrdYcddsmQhQEL5otK2EXC4bBeNsHJiMaCUd3xJ21Ypzevy56i2u6W6g477JIhCwNWPVfdIgcLDQUS6p9I2Vdcb9hh9ygFnPkHMbkOQtw6wYIAAAAASUVORK5CYII=", "search-focus-v.gif"], + m: [u("search/focus-m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAcCAYAAAC+lOV/AAAAUUlEQVQ4y2NcsuPkfwYyAcu9z2wMZGt++JUCzczv75Gl8f///wwsvhaqZGseBaNgFIwCygHj+cdfyC96D11/RX7pCbSZfM1SbF/J1yzDTr7NACn1GmiOSs0vAAAAAElFTkSuQmCC", "search-focus-m.gif"] + }, "0 14 0 20", {inset: '-3 -13 -3 -21'}); + }, + + // list + // list: function(rowHeight) { + // // return new uki.background.Rows(rowHeight, '#EDF3FE'); + // }, + + 'shadow-big': function() { + return new uki.background.Sliced9(shadowData(), "23 23 23 23", {zIndex: -2, inset: '-4 -10 -12 -10'}); + }, + 'shadow-medium': function() { + return new uki.background.Sliced9(shadowData(), "23 23 23 23", {zIndex: -2, inset: '-1 -6 -6 -6'}); + }, + 'table-header': function() { + return new uki.background.CssBox('background:url(' + uki.theme.imageSrc('table-header') + ');border-bottom:1px solid #CCC;'); + }, + + 'tree-list-header': function() { + return new uki.background.Css({ + color: '#718193', + fontFamily: uki.theme.style('fontFamily'), + fontSize: '11px', + fontWeight: 'bold', + textShadow: '0 1px 0px rgba(255,255,255,0.8)' + }); + }, + + 'drop-preview': function() { + return new uki.background.Sliced9({ + c: ["drag-c.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABwElEQVQ4jWNgQAK+aXdt/NPubvJPu/cGiP/7pd77B8NA/je/tLtbfFPvmjKgA8/o23z+qfcmgRQB8V8oRjEAjtPufvRLvVuEYgBQoBNowB+QgsCM+9ejSh7WF0187ti04LUxCBdNfOIcVfygNSD97kOw4SC1afeaIJpT7rkCnf0BJBGcfX9r+aynVjCN6Liw75lbcOb9oyDXAfX8AurVA9m+C6Q5IPPeTZBNuDQj48D0u49BeoAuXsgAdM5bECe69H4jMZpBOKroQTfYgLS7TxhATgFx8nueuRNrQGbLk1BoQH9jgIV67fznpsQaUDn9qTUsMOEGEKsZhKtnv7DAMKB02jNbYg3IaHkcDA3E78BYuPcFHIhlD6qJNSC84P40qAFPQS7YCUpAAen3nhETjSX9T5wCMu4+giS8u3MZ/JJv2cFiIijr3sHSSc/tcWnO7XjiDVRzGpKk770DJyRoUs4HZxawS+6+jCy+35nf89QVHGBznptldTz1BTp7BthmSPz/9k25V4WSH3xT75SAkyck5/3FhUH+9k+7k+eQsJ8DLU82MIGyqn/q3TWg7AyyBaERbPBToEuXeKXeMkDWBQDc2R7fquJzGwAAAABJRU5ErkJggg==", "drag-c.gif"], + v: ["drag-v.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAECAYAAACHtL/sAAAAHklEQVQYGWPwT7v3F4SbFrw2JgWD9aXe+8Mw9A0AAKdtlPmxoeQnAAAAAElFTkSuQmCC", "drag-v.gif"], + h: ["drag-h.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMoAAAAQCAYAAABXwODhAAAAU0lEQVRo3u3TQRGAMAwAwXjCR1EBQqoEZTGABjp1kMmPzz7WwM1cjCsXUAsRoDHKfN4DqBkFjAJGAaOAUcAoYBQwCmAUMAoYBf4e5bzzA2pGgYYNtJpkoHuF6t8AAAAASUVORK5CYII=", "drag-h.gif"], + m: ["drag-m.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMoAAAAECAYAAADPh6BsAAAAKElEQVQYGe3BQQEAIBADoD3WyIYmsZ8lbOAVALrPXQG+GmDUAKMGGD34YQJREnniOQAAAABJRU5ErkJggg==", "drag-m.gif"] + }, "8 8 8 8"); + } + }, + + images: { + }, + + imageSrcs: { + x: function() { + return [u("x.gif"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQIHWNgAAIAAAUAAY27m/MAAAAASUVORK5CYII="]; + }, + 'list-selected-row': function() { + return [u("list/selected-row.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAARCAYAAAArDQkmAAAAN0lEQVRYw+3RAQ0AMAjAsHNVN3atyAEbhHQS1ng/62hM1wIgAgJEQIAICBABASIgAgJEQIBIy2u67AKjWqm8DQAAAABJRU5ErkJggg==", u("list/selected-row.gif")] + }, + 'list-selected-blured-row': function() { + return [u("list/selected-blured-row.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpPU2iAAAAGUlEQVQokWO4cuXKf1Iww6iGUQ3DWAOpAABAkpxfQrJxGQAAAABJRU5ErkJggg==", u("list/selected-blured-row.gif")] + }, + 'list-unread': function() { + return [u("list/unread.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABJUlEQVQ4y2NgGAVYQc2qG4rtu17Wtux6ebxt1+sfILpj54uahuX3FQhqzmy/pFSz7OHKju2v//fuevN/4t43YBrEr179dFlB+3W8hjDmTrjSWL/6+f+Ora/+d2xBwkA+SDx/2p0afAYwp3VeP1255Mn/mhXPMDBIPKPz9nF8BrBEVVz6WTjz4f+SuQ//F89BYBAfJB5TfvkHXgO8k46dSWm//T9rwr3/mUgYxAeJ+6acxOsCZsegrW3+GWf/R1Rc+x9bf+N/QvMtMA3ig8SdIw7V4g1Ec+cZWk6hO9Z7xJ7875V49r938gUwDeK7RBxY6Ra6U5FQTDKZO0/SsvfZ3OYYvPeMS+jBn87Bu0/b++9qdAneokRsWmIEeQcUJlDMDBUbhAAARh3Ic97iQwkAAAAASUVORK5CYII=", u("list/unread.png")]; + }, + 'list-unread-selected': function() { + return [u("list/unread-selected.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAASElEQVQ4y2NgGAVYwf///xWAuBqIjwHxDygN4isQq3npf+xgKUFDoDbhA9WEDDhGwIBjhAz4QcCAHzR3AcVhQFksUJwOhh4AAMVYJHi3XECRAAAAAElFTkSuQmCC", u("list/unread-selected.png")]; + }, + 'list-attachment': function() { + return [u("list/attachment.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAlElEQVQ4y2NgGCpAFojbgHgmEGcCMRepmvuB2BmqEWRAPKmarZDEDIC4mBybGaB0DRD7kqIZpgGmOZ5UzfHU1GyALwYIabaCysviMgAUz5ZALAzEpaRqVoAqBgFWINYC4nJiNcOc34LEZwdiDiDWI0YzsheQE4wdEPcRq5kB6ndQCisA4gwgjgViMXIyjjgQixCrGABccB1YrtCQuwAAAABJRU5ErkJggg==", u("list/attachment.gif")]; + }, + 'list-attachment-selected': function() { + return [u("list/attachment.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAm0lEQVQ4y2NgGBLg////+kC8GYiPAPFUIBYkVfNuIE4DaYQa0Emq5igkMR8gnk+yzVAxkAtWAnExKZrBGpA0d5KquZNqmqH+FyRXcxRUXh+XAaB4jgBieSBeSKpmY5BiKJsTiJ2AeAlRmpGcvxGJzwPEfEDsQVAzmheQE0w8EO8iSjNUA8jv84F4DhBPBuI2IFYhJ+OoAbEiseoB7WhRVygLSMwAAAAASUVORK5CYII=", u("list/attachment.gif")]; + }, + 'attachment-header': function() { + return [u("list/attachment.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAuElEQVQ4y+XQsWoCQRAG4M+ca+49UtkYLAJB0JN4IAd28QXyIoE0SWebBAu5SqsUeUKbEa5J3BRWGdhiYb6fmeFfVB9r7OMtUeTiAkNsAtYRMvgL/sA9rrHIDejiWeAbHPB4boUurvGFEd5xGzfJwg+BV/iMSRJ6P+ErTLGL5ldUgRf4xt1vAQkvaFDiGdsOnpwbf4AW42gsMc/Fpwne8BQhTVw9C59uUEVIG+tMc3E3JMU6Kf6XrSOKlBrzbNjuNAAAAABJRU5ErkJggg==", u("list/attachment.gif")]; + }, + + 'table-header': function() { + return [u("table/header.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAR0lEQVQoz8XNORIAIAgEQbn+/18Bj8TMMlgCJ6Wa1blrYFqwTT38fjk/6YEzEl/O/IVHBXd3HBMRjnc4FpFPyyVsZjhmZhgvHLIaOS+PnNYAAAAASUVORK5CYII="]; + }, + 'splitPane-x': function() { + return [u("splitPane/x.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAMklEQVQIHR3GoQ0AAAgDsP1/AxaNhSMIgoNGhmhSzAzN7Olwd1bV0xER7O6nY3eZmU8/oKwy0Tv1fE4AAAAASUVORK5CYII="]; + }, + 'splitPane-v-bg': function() { + return [u("splitPane/v-bg.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAMklEQVQY083KMQEAMAgDMA48ohArNQUts9BzuZPdfWHIqgorSvIiSS/urhdn5vsIwIoPCOcbF8QKbsQAAAAASUVORK5CYII="]; + }, + 'splitPane-vertical': function() { + return [u("splitPane/vertical.gif"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAADCAYAAABfwxXFAAAAE0lEQVQIHWMsLy//z0AOYMSnEwAIngTLoazFLgAAAABJRU5ErkJggg=="]; + }, + + 'tree-list-header': function() { + return [u("header/tree-list.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAANElEQVQ4y2NgGNagHQ1TZADFLvAl1wAGqGayDWEgxxBcCn2JDQ+aOLmdEkOoFpX0T4n0AQDoThJhbb2E6gAAAABJRU5ErkJggg==", "ThreadListHeader-m.gif"] + }, + 'unread-header': function() { + return [u("header/unread.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMklEQVQ4jWNgGAU0A4JAHAzE3VAcDBUjGoA0LEPDwaQY0I3FgG66GkCxFygOxFFAAgAAoF8RlRhg/AAAAAAASUVORK5CYII=", "unread_header-m.gif"] + }, + + 'toolbar-get-mail': function() { + return [u("toolbar/get-mail.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAUCAYAAABxnDbHAAACKElEQVRIx9WUy0sbURTGtSIIjRtxoYRCQSi4EbHIiBQqFApdhO7c2H+gK83ateAqm0JLuxiQgrgRxC4EUwqFkpYq2tIQQzQPhsS8M0kmIe+cnu+aBBNNk+iU0gM/hjtzz/nm3vPo6/sPbJB5wDy8xAQz8DfE7jD3mEcmk+lZOp1+X6lUjuPx+DtJkp7y+3lmXE/BUUYyGo1PFEVZI6JkLBYjn89HiUSClxRyOByrvGeBmWVGbiM2zEwjmM1mW+bgZ6qqksfjIafT2SAUClEqmaRqtWq3Wq0va+JTzN1exIaYSTjLsvyiVCp9yWQy5Pf7yeVyNQFBGL4Hg0HKZrNULBY/WiyWRfZ/XMv/YCfBfmbObDY/5wDbhUKBotEoud3uK4TDYSG490Ojk0BBCOIWcPVs5VQqtYH8c7yZjqIGg2EBXsgXglxHJBK5EPypkfxZFdSFvV6v2JPkK+diIxyim+udRsB8Pi8ccSJcYx3kD7Z7EKM3+4Em7Eq24YcnrFYXHU2iFsvlciJQXXDn6zm9+uBp4u2ejw5caqsrRKUbiV62rU+ntL553OD1jp2+n4Tb7tdFFHbkDJC8e0jffimdtt5OlPuQeBIRV6UoEKyRN6w1TdNflHuPeOSJ6rzO8B7fuaf1EUVAFBB69k+GU6OnW3+sJ9FyuSx6td3p2qUA0wnjEinoSRRCmCz485sCf7Rat6Jo5iVmRQeWuh0OGPhjzH0dGKvF+/f2Gzk0rH9bwnSYAAAAAElFTkSuQmCC"]; + }, + 'toolbar-delete': function() { + return [u("toolbar/delete.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAUCAYAAABxnDbHAAACbklEQVQYGe3BTWgWBAAG4Of73PZNwjnnWqRkMAYb5ikr25rSCtyILLIMMaIWko2ZNbdDSj9iBlISlnnqoE0i6IfAEtKgg9ml0tqyLvZDuShBGzhHThtvhw4eOvlthw49D//7t2Ie7e7LQP/Hee6Zk3lx+1959ukfM9B3IGu7u1E0jQpZtWph+vuGs21rsuuVZO+e5NDB5I09yWuvJi88nzz5+JHxzs6rTYPC+PLli7O+Zzx9G5LennPfrLhz/5utS17a1NS08e221p1DK+/+6MKG9RPZ+ETSu+7MWGfHdSiYgqq03fBVbm1LbluatN84un9+wzo0oxEtuKln9qy1Z5a2nkpHe3LL4iFUKlNxkoGQcPYXRkLC+AT3oAol1KDxIR4If4b8wRoUlWFmOBpyiMEuHj7BiZBwITyCIgqoRuOXvBMSPkBJGWrD+ZAdPIiWJjqHOBoSEjajiAJqBukNCSdR43Kt5pqQkAFacQXmVbPsCAdDQsJuzEBpN10h4RzqXKYCrgoTIe9zOypQgQYsOcC+SRIS3t1CzWHuDQm/ol4ZrjzNtyHjbEGlf8xAHa7fy8vjXAwJn4XXQ37iE9QpQ+2H7AgJv73FPJcUUYtF23nqLGMh4XzIIP2oUYaZWPQDwyHh8DBzXFLELLS8x+ZwMeR7vkMzSspQRP1j3H+KsZAwEjaFjtAYusK2MBoywuk1rMRcFJWpCtfex+rjHJskISEhISET5As+vZm7sABVpqCAaixA+z52HufYCGO/k58Z/ZrPd7EVrZiPEgqmqIBKzMVCLMMdWIEutKMZc1BhmhVRjVrUowH1mI0Siv7L/gYT9B/L2Zyi6AAAAABJRU5ErkJggg=="]; + }, + 'toolbar-junk': function() { + return [u("toolbar/junk.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAUCAYAAABxnDbHAAAC0UlEQVQYGeXBS2tcZQAA0PN9987MnckkadrYptKm0krBBkt1IYKI4EJXXYiCUP0LIgXRjQtB3LhyoVAXggguXImKUHVVcFVLKZSCiI+aoI2FPjSZzPPez0dxYZxp4tpz+L8Itu84gskSPrENua0dd0uWUvrQBCGEJ2xTbmvh1PkPPrKFk08dfPCFE0fmRimdOfDkp9+7jdwE377h6ciJ05ePNmzDw8f2vHT265uIfjhlOfWdTrx26HnLNslN0B84U9S9t7Azq1812btfXvSXR972p13nXjW1+Mzixo/vP9r5xXPGiCZYetHqoOPjsrfiv4nK7hXdDSeXXjEwRnAbF1/2WCh91pwnbx80WL9muParNCSVpEQaUFbkBbW5ReX6sv5NX9z/pseRjJGbLKwPFHMzVP4QqLXnZFNtUQ2RNFSNuqrqGqlpcGNZSJT8hgwjY+Qmyze6ji4skjXJWnNCmCL6QyBFIZWqqqvszSp732kuUPYZVpbQwMgYuckaw75jVUkoyQ3EYrcQa25JJIJpsVaIjQbVBtVltbZ9aGMDySbReAFFIziYSpSk1CcEf0tVKY3WSCMhqwtZTloTAo1C+9n7LJggmixv1e2rzdbUZu5Way/Ji72yYresMS/kLSmVYpySFXtkzf1icVg+vVc+zQMHHEFmjNwEhwutorArb94jNhfExk7yppQqoRqSEikhCVlTJgkhinlLNbxpx2z3EHKMbJKb4KH97hj29PLpe1uxOSPmbUIkDVWDoVhrC7EuNnaItbaQ5qRUqUY9w7UbprPzdyE3Rma8eOE675z1zfRye2p+70zr+kpvozPQWe+WnWs/r3dWLvU6/dV6FvrNvDG7Q2xN65e13srVjeVzF1a/ev2tnz6/wiUMbBKMF9DAPHbjTsyjjuCfRuhigD766GAFqxjYJLi9iDqaKBD9W8IIFUqUqNDHCMkmwfYF25Ns4XfmhwQY23b+DAAAAABJRU5ErkJggg=="]; + }, + 'toolbar-reply': function() { + return [u("toolbar/reply.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAUCAYAAABxnDbHAAAB5klEQVRIx2NgGEKAkZ6WMQExt6urq5S5uTkfPSxnAWLRqqqqmI8fPz6IiYkxAvKZaRmUHECsvHv37ll///799R8I7O3tPaEOoToA+UQgISHB9eHDh+f+IwFTU9MwqGOoGsRsQCw9f/78qi9fvrz/jwYaGxv7cnJyQL5VA2IJUFxD45z8xKKtra1//vz5Lf8JgD9//vx49OjR6ZkzZ+YB9cmR63vWadOmeb579+7RfxIBKAr8/PysgWZwkWopR11dnf+TJ08e/icDgBwrLy9vCI0e4n0KSqmsrKyJa9euPfUPCEi1+MyZMzuBZsiQEswghZxArATEQQUFBYvevn37Gd3g7OzsTY6OjpMSExNnp6SkLJwyZcr+y5cvPwZmqb9A6b9FRUXh0PgluTAQA2JbNTW1qrNnz95FtlRTUzMfKAcqIEBBaQ7E7kCcCMy/vfeA4Pjx4yug+slKybxArA3EcVOnTt318+fP30j5lBMadyAfgYpFSSA2lZaWjr958+ZeIFuBkhIJZLAsEHsGBARMePHixRMXFxd3LCUSrPTSWLFiRa6SkpIcpYUHqHQSBPlEUlLSnY+PTwVH2QuyhFdISEhm4sSJcpQUGOiJTIBAIQByDLuvry8XNSxFtpyRCDVM9K57BxcAAFmihZEgAelPAAAAAElFTkSuQmCC"]; + }, + 'toolbar-reply-all': function() { + return [u("toolbar/reply-all.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAUCAYAAABxnDbHAAACy0lEQVRIx2NgGOSAkURxigATEPOmpKTIAGkuNHFuV1dXKXNzcz5qWs4CxOIdHR2pX79+fQJkKyKJi1ZVVcV8/PjxQUxMjBGQz0yNoOQAYpV9+/Yt+vv375//QADkW0LFlXfv3j0LKP4LJG5vb+8JdQjZAORiwfj4eLfHjx9f+g8FQAtAlnomJCS4Pnz48Nx/JGBqahoGdQxZQcwGxLKLFi2q+fnz52eYob9///6/bNmyMz09PZO+fPny/j8aaGxs7MvJyQH5Vg2IJUBxDY1zgomFW1VV1eDy5cs7YIb9+/fv//fv3/8DLTu3a9euM/8JgD9//vx49OjR6ZkzZ+YBzZMj5HvWKVOmeH14//4xsiG/fv36BxQ///z589f/SQSgKPDz87NGS/EogKOsrCzgyZMnD5E1fvv27e/EiROP37t37/l/MsC7d+8eycvLG0KjDdOnoBTJysqauH79+jP/QOEKCa7/wMT0v6Wl5eqKFSsuw8RJAWfOnNkJNFsGWzCDBDiBWAmIg4qKipYCg/orSBMwQf0HhsD/hQsXPs7MzFz79u3bz+gGZ2dnb3J0dJyUmJg4G1iILARGyX5g2nj8F5TkgQkfaF44NH5xFgZiQGynrq5ec/78+Qew7AIMKlCWSVNTU6s6e/bsXWRLNTU184FyoAICFJTmQOwOxInA/NsLjJp7x48fXwE1F3+xB8Q6QBw/Z86c/cAs8wcUsqB8CsTaQBw3derUXcBQ+I2UTzmhcQfyEahYlARiU2lp6fibN2/uBbIViCmR2KHJ3is8PHza69evX0J9Ac7LIAcEBARMePHixRMXFxd3LCUSrFTTAKaHXCUlJTliCw+QQcJAbAEMQm+oZfBSC+QTSUlJdz4+PhUcZS/IEl4hISEZYC6QI6bAQA5uLqgl7FgSnwCBQgDkGHZfX18uUiyFWcCIw2BGIoKNEWohI8OIAgAdEkrM+VJMxgAAAABJRU5ErkJggg=="]; + }, + 'toolbar-forward': function() { + return [u("toolbar/forward.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAUCAYAAABxnDbHAAABXklEQVRIx2NgGAXEAUa6WmZubs7n6uoqBWRzAzETPSxljomJMfr48eODqqqqGCBfFIhZaG0pi729ved/IPj79++v3bt3zwKKKQMxBy2DnMXa2jroPxJ4+PDhuYSEBFegnAAoJCgxnAuIZYFYDQ1r6ejoZP5HA1++fHk/f/78KqC8NBCzkWupyn8ywPnz57doa2vrk5vIrP6TCT68f/94ypQpXkAzWEm11JFcS588efKwrKwsAJq4SAKWpFr2DwjWrl17ipWVNRGaokn2qSIQxwJxBRqulJWVnYFu4du3bz8XFBQsAsoHAbESEHOSk4VAQSMBxApoWMXIyCgN2cKzZ8/eVVNTA6VcWyAWo0VhAc+nP3/+/D116tRdQLE4INYGYl5aFYssLi4u7i9evHgSEBAwAcj3hOZnNlqWSMx8fHwqkpKS7kC2KRALUloKEVXLQONbgNzEQonFdK1PKQIAnghowXUWSasAAAAASUVORK5CYII="]; + }, + 'toolbar-redirect': function() { + return [u("toolbar/redirect.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAUCAYAAABxnDbHAAAB40lEQVRIx2NgGAXEA0Z6W8iZkpIiA6R5gZiJXpbKffr06e6kSZMygWxJIGalh6UmX758+fT///+/x44dWwnkq4J8T+sgt/rw4cPH/1Dw7Nmza7m5ud5AcSEgZibFIA4glgBiFSBWI4DDkC0FgZ8/f35evnx5PSjogZiNWEsl3r17d+M/heDy5cu7LCwsjIDm8RCTyKQ6Ojp6/1MBvH///smUKVO8iElgoOTvc/v27ceUWvrkyZNH1dXVgdAowwtAQaEKVDyTEgs3bdp0lpWVNQloljKxWQkUD15A3z6CGfIPCBwcHJYAxeuAuAKKe4Hx/wXZsg/v338tLS1dAZQLBmIlYrIQyJdc0NTrkJWVNRfZwN27d+8DipsDsQIUBwAt+YSUeB6Zmpo2AsXtgVicWB+yAzN54P3791d+//79A3qQff78+SMwyMyQ8qA1sHD48hcIFi5ceBjIBwWnLhDzkVIsgvKVFNQ34WVlZTP37NlzCVjUfYNZPHfu3HqooSBg+Pz588dJSUnTgWxfIJYHOZzUEokJGgegEkURiC2AOASIC4qLi5cdPHjw9PXr17dDgw4EpNXV1T2gjhQBYhZKizhsDgBZAApeQVh0ALEANB1QvZaBOUAAGrSsSHUp40DUqSQDAL3v89667EwpAAAAAElFTkSuQmCC"]; + }, + 'toolbar-new': function() { + return [u("toolbar/new.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAUCAYAAABxnDbHAAABkklEQVRIic3UMUgCcRTH8X+QBUEIQS0FLRY01lBDk4S0tTfVYELQZNTiIlK4BFGb1VJD0NmQhg4lNqSgixkUhp1FWtEgIhbUkeevdxmSENn/9KAH3+nP4wMH7xj7hzNWR6pnHCpG2Tux25vh9S5ROxAEPQ9qVIPqW5tNd6HQNQIBEAp4PAs8qIlbLMm4CK0/ZDIZBHw+PAtCnlCDhqgMpOYgxUZw5HdjZdlR8jmdkzwgJ1oGEWEoHTI8rrXn982D3CAHqoCzFfBtlRXEKTZK+z1UL9XWYLQalLcYbENskXb7qH7K0GCUQNFSAYubDE+xoHIyM9QA1U218IC10W/g+wbDzakXiURCQS1UJ9XEC/6OSvdA0vwJSi6Gy2M34vE4UqmUgk6owWqiL7cCcO7A6/Ywzvy7iEQiUG5TlmUFNTUcFZNXcNqmcbDnop9OANFoFLlcrvKuCZpOpxEMBhEOhyGKIiRJqnrXBC0UCshmsygWiz9+ek3QWlMvamTlm7NS83/M+rWnenRUB9XFma4eVJP5AH8kM5mHjOhWAAAAAElFTkSuQmCC"]; + }, + + 'arrows': function() { + return [u("arrows.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAABAElEQVQ4y62UvQ2DMBCFvQFsQDYzm5gN0kQCV6YBpDQ0MS2jMAIjOGfACrbOPhd50quwPt3POxi71E3L2qr5wQgZY1Yw+Y7JaTGHx6V/qrlMAJ16cEkDD6je20E3GNj42sENCvaAl2EMG4DrBNBpA9ck8GctCKCTICuU44dnVsiTM7RVSfkuMmYowEV0ht2kFQZCgAoF3XP4GnSVmUPy3f9kK0tt2H6/VZbS8Y7ZNlNANwbbJgGsblXCItDqtArmpyIw5W8YtnpGJYhOsG271SsqYXSw2EDuItcRQEX0OpAFbe5CiOhs7kLSvzA4s7Nd/9wQIL+APOO/qEVO3GKtfgEB6EwzIYKPOgAAAABJRU5ErkJggg==", u("arrows.png")]; + }, + 'tree-selected-bg': function() { + return [u('tree/selected-bg.png'), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABJElEQVQ4y61UQZLDIAyTZrhu/3/sD/YjfdIi9QAhxoGmh80hEEMsydaYz9+X8Y9PeTx+wPFJAO4rYBgEYXhxuogQKJJhr0mShC2QHcDusb2oIilgOqAC7T8j/h+TZQUAUP6qdgJS+oVkErDa2hlMksnGKpyP2NdNqdLJyGkFQH9guKvhkSAzvGhOz6UkTE2xWjYrICgirJOOvYxSq3N4sffGlxyx0OWafOcPviOAU9GRmgGwtMucPHYm8PDiSoE6oIJLSq06r6QqX2x050sQRRN4Pz6Y9lcEdMjojujRNPUa7hglY39j/GJ5rk5mFMqWjZ/3ocvcTJs7Rteqllo1zbN5OuQM1/nX7pwAxQYMrXzcjGsN4xrqZl7fB5gl3xsj+nA10d9McATavUOaqgAAAABJRU5ErkJggg=="]; + }, + + 'mb-sent': function() { + return ["SentMailbox-m.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACBklEQVQ4jb2TTYhSURiGbZh+mZSSYaJBahVMUTqLNrlr1aYhksjCoEWzEVsG4qaYAjctgyBbmMigC5PxD2lr/lQgJDjXjZAi+ANqctPR8V6/3nPnKLepIWjRCw/34P3e777nO0eN5j/rKNCpuPQ3wzxYAIvAAM4lEglzp9N50e/3P9hstmv7DXPgBDgNlpmBPQVBuDkcDl/KstwVf4g0GAwom82+2Z9ggRsMVqvV0Gi17kqS9I6I+oB6vR7VajXCl2k0Gn3T6/U21J6amZ1O52q9Xneg9hMYEddYGlOlUlHMbM3k8/mew3NVPZjVQCBwOxaLrW1FtzaCweCraDQa8Xq9nwuFQovFngpJPqL+Djg+bXAhmUyuIxaJokjd711qt9tULpepVCrJqVSKVJJcLteTX/aOvaxMJpOv0woMSomLwY0bjQZlMhnCAJV31Wr1PSy3+OnMdB3cM5lM6/F4XI5EIuT3+ymXyynNisUiNZtN5u+YzeZHfNB/FkwxFjkcDtO2IOwyFwarJMrn829RcgMcOrBBKBS67/F4yG63f0mn05uswc5wh53CtlarfcAv1G9iHY8AndFoXLZYLK+xvuxwOJ5N5+J2uzfw25pm74Kd5Cc3p27Abt8SOA8ugivgIXgK2JcfgxU2c147r94KWxwGxzR7f5DZFQZnwRkeXceTHjyDf9FP6cxD9Zp6oCUAAAAASUVORK5CYII=", "SentMailbox-m.gif"]; + }, + 'mb-inbox': function() { + return ["InMailbox-m.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB1UlEQVQ4T62RTUsbURiFg4i7EBTp3v6FIi5KiP+gLV249j+0m+66qHSpVFBwZZW6SMSYtLQ2KY5KTBkTyKeJaSedJMaJkXQ+AmkyzXi8b0jC5KNIoC888N73nnO4HxbLf6wJxiPG7D2QZnxYwINAILAiSRL+VbQXjUZ3mXZqwO1yuZ7ncrmzcrmMhZfOodBeUSoW97x7T/v94xzHvapUKs14uoAXbw+HEknkwTTgef4184yZA6bP0+kNRVHg5VJYdkWw6kn08G4/gU9BAaQRBMHJPJNdt9vtfiYrMq9qVay5Y1j/nMLG14tBDlKQFQ2yLMc9Hs+Tjn8sFAotqaqaT+dumDCD98cCI9vD1kkWm0cCkuINtKpWCYfDbzrXsImiuNNoNOCPF7EdFLF9+gs7fL6HzsyXkEDaQqFAv2GjgIe6rpfM3/UtJWE3dtUDzfqLeWcoYM4wjOp38TeIZEnDh5MMjn+U4M9ct6CeZrTX0bF34MhrsVqtjyktL9e6+PgsuPMrfIxdtqCeZmYNFXnpBHZalLR6l8tyFb7gTzi/JFtQTzOzpn0FOwU4aCHX9C5q/S/+NA0Yt2hBPc3MmnaAgwLmaVHTmyPRDpi3tI+xwFgcEfLY7wCqtGPkrXAP2wAAAABJRU5ErkJggg==", "InMailbox-m.gif"]; + }, + 'mb-trash': function() { + return ["TrashMailbox-m.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACdUlEQVQ4y4WTTU8aYRDH99IP0VN76Sew6UFDiBcS+4JBJQKuScOyVViBQC0IS3lTlqUJoEINarGtEkkTTY3WphfTNDVtSloubQRUhAWe8VTTj9BZD22ihU7yP2ye+f9mnplnKepvXEkkEjpBEJamp2eKk5OT0tjYGJjNZuJ0PmxEIpHvqVRqE8XIudTFiMfjOYvF0jKZTNBJHMe1RDH26hLA5fI05ASapqGvrw+0Wi0oFArQaDTQ29sLer0eBocGzyEej6dxCZBMJj/p9QbS09MDOp3uj1n+lo1KpRIMBgPg1Ug2my1cArjdbu/i4iJEIgIYjcy5uavrJqhUKtlMGIYhWBmSiRSk0+kQ9a/w+/0kHA43dnZ2m8VikRQK30ipVCblcrmZz+el2bnZGnYKVLvAqUuiKBzhJo5Xnq9U0SBls8+qaKplMplqNBqthUKhjoCS3W6vxWIxCSH1jY2NYzRJa2trdVEUmwiSAoGA1BZgtVo/Y0LJZrOd4L7rPp9PyuVyh4IQqcnt47mEHRy2BWD1XZZlj7DaCc5DWl1drfI838I30nA4HE25OkKLbQG4opdTU/zB6CiDV3hCHA4PBIMi4TgbwcdD8FVKOOQ3bQFery9z987Aj4mJR3UTYwEfPwNOhxdXmwQL5yQmhqsEg9P5DgA/zxjHv6jV2qrV6mqOjLDAMDZQq3XAPrC0+vsHK/Nz6XhbQHe30ry99fb9wsJShWXHWzRtJC43D/NzT2F5+UVzZ/vdEU3ff0x1iOv4L9j39j7sbb3ePdjf/1o7Pf15cvbrrLy+vvlxeFgfxZwb1H/iKuoW6jZqADWEuodSoK5dTP4Nv1tkKI/PLDEAAAAASUVORK5CYII=", "TrashMailbox-m.gif"]; + }, + 'mb-drafts': function() { + return ["DraftsMailbox-m.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABlElEQVQ4T42SW07CUBCGG12DqyDeXnnABbgDN+BqgBXog0++QeHRBDUQEyRACDe5RqAXoFAqFKTcxpkjbWwLxD/509PMP1/nnB6O+9XZHnvQp+gT9BF3QJewQ9lSvUBPVVXFQCDgOQTxOptlVYfX95L1PhwOpUMQnxlco1u9MTREDRKpog2Kk8j7IAywWm+gKoyg3Z9AByH5ch2cwkm6fr//HHuObYBvYwX5pgLSUIflagPFTwVa7Y4LYEKCweCFDZCu9KA/mrGAONBxijEIgrATQKLt2ACabrDCGreRaygwN5bQVxR4TiQhFOLh7uEReJ6HSDQC0Wh0EA6H720AkyyrU6jhAZIWiwXEkmU2STxVgkyhxtaSJAH2XLsA9AfS1R5MZoY1aqrSZc/xdI61LsuQsOfKBVC0GWTrCvxV8kO21lSjzBbgcwHo6/JItwHeipK1phpldgK+8BDjBdEa0VQ8L1prqlGGsi4AjdeUNXDqJWe/Cw1JY1txAZ4yLXaBnIpl7QDKUNYJ8KJv0Lf/9M22h/sBFOp0+67glEsAAAAASUVORK5CYII=", "DraftsMailbox-m.gif"]; + }, + 'mb-folder': function() { + return ["folder-m.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABvElEQVQYGQXBMauXZRgH4Ot+/u85uEaFBCHi4GKBEQ62eIZaGnLWST+Bn6HFwa0PEPgFgjhDiw46OUjg4ByOQYNUyvG87/vcP68LAAAAAAAAAECBr3764frJrXsA8OrZ88denz4BAAAAWOD6ya17D+7cvgtB4Re8en36BAAAAKCu3v/54Zeff3ry43c3LlcB8MeLl2/goEABygCtvfn7n+fLpc8+ufnowf2bEkAzhpNvrn2xZSdszZpJx+zoDo48+vXxusDb/9/TE9AYumPrXaG7nKeloxNzxhjH2rBs+7r/dzZV7QYUSZuJ7ujQpoSEdLSWPea+bcue9Fqx7NOsEiREJFRiNkkEnQi66JYlM+bGnE0VSEhiiiQSWqRJSGFMWzrLrr1dpws9BQCNDt1tKJ1oqIC9j43uWjpjbOfnph0EkBDRDUBQgDlWa41aZiz/btNRdqWAEEASOxaAQtocQ+RoaZyds2UXw8QoMjmgDwRHCIJDWJeSZqnurOsH1WdaaQRpGgYNgGBim0hneffh7P1vp7+/UBQAAAAgAAGOq9/VlW+/v/TXn09XHHDAAQMDAwNBo9GYmBe/vnH8EaMMKbkNQ17cAAAAAElFTkSuQmCC", "folder-m.gif"]; + }, + 'mb-folder-empty': function() { + return ["folder2-m.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABOklEQVQ4jaVRO26EQAxlJhPOQIVWXIKWJg1HSJWWgisgqpwjEoegSBFSRVwCbcMNAkh8d94EI+9I+axiyfPx59l+dpx/isARhuFDFEVP3FFV1Utd16+/ASgcSE7T9NF2/gVAJEny7HleFMfxiTvKsjybACG+TW7b9t3J8/xtu1GWZTF3lmWVIrR1Xc2t7aYq7nEcj2p4w4Y4qFJfqWqaprnve2Pk7eoqxkZJBE72PWZS+rPO83wAIIiC6W0lcdumqAJvl0bi1elPoLttU6jedd0RQELBvDMCI7/+CzAhh2G4cnIAtCqlvCKaRtB+gREUSCT2OQAHsn07F/dmF3oTDkax18iT0AWBUUdmjSACIwDkFnFdF11sSid3RVF8UJWfxOZJx3+KIAj8pmmwwzum0lIQsjIFAYvv++4FKBNU/tlKtPcAAAAASUVORK5CYII=", "folder2-m.gif"]; + }, + + 'dragging': function() { + return ["drag-m.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+2W/2sbdRjHa3W4IZu6yfxl/4A/+R8IMkEUhCn4k+4Hf3V0DFxBKyVFa7vYmrSrZnkimfGWLPR6Np6JabOV5kaIhqMhEALhIBwcB+HgOAgH4SAcnM/zaYnp9yxr6i8GXtznPvfl/c7zfD7PcyMj//+e4be1tXXmPzUQT69dnbv/8NNTF15fX784E+GvBzkxJDwuwp1wfJ+JqampF5Jr0oc4fO7EDfy0nP4gsvLoXqfTAdd1gScToX9NpB/9dXUymgoupSR48uTvN05MuFCtvhp6mL4WFnLfG6YFitaCbEEDx3EgmZUguvLHmD+R9vFSGZyOC03LhrnEmo/n+XMnYiCYWPkomnoMDd2AYqUJXy7J8EWgBLlCA0RJhrAogdFqg+d5UNY9diQzFJFnEqZ8RpYz7/4QF/26YeI/t8AX2hYPJIowm8hCVTWYoNryYD7VBl9MA7nhQdvpgO8XMZjP518ZSFwUxfMzkcT1xUQGSJzC3bLbEOBK4ItmICvXmDBhOR4TngjX4ZuYDv6kgWnwYLOswAMx/8lABu7/lnln/tfU3ZZtM3EDTcQw31yuBJbd7ooTHdeDAK/CNNeEyZ8VjJAMUsXGeRf8GKVsPn+lb+GNjY1LC9zqx8GYGDQtC2wUyxUrKJCDmmbsEqaI2GiODNR1hwlPhstQqJgsBUSppsJC8s+blM7+Fhy3veBotctVBcMqsHDb+DISdREHBU1cdIqqISoYuOqbeL+wieOWjdd2Q+ZX05tvHise5n5/fyGxekeu1mGWEzHkRdAMC1qOi3lG2h0wbIeteLVpQk1RoVJvsHv0HrQ9lBUNvg4nv53juJeONBCI8bCEbueTWZDxIc20Qbfa+BIbVKMFjaa1jY47ArdkTdWhjmmhsaIdTUjYhHu4ow4RFuC0wOJ0cZf4d5HUe1TNehfXsEhmCzCxGB/rio8H42OzXBa4jIS13QNd16FUksHEBUVHRWmw+UG4Nl7t8tm8y+ZI51bwwe2ugekfl9/6CoRQVJRYc9E0HcVNBo3JEM0PAon2QnOksy//n89Eboexe1HROEn2RoDmSGd/vb8bv0Ltk7qYivu6iRWPKBZL3XO69rTsjQDNhYQDDNCHQ0CQBhJ5WhYPMTDi56Vu6TyKSrXGImJaLTZuYBUk+nmWIJ0DDUwnJVZqhw3pHGjAl0ADbedY9KYBKu6OcqXGxhQFmqe+0c/zpHOggYmYhC3WORaVyi/Wf9YDanV2To2I6n8/z09whxgY79PAYZCJfu4jnX07ABm9FZXAxBuGDemg3vO9n+yjyNkbZABb7LC5sW3g3I4uc0FfKRfevrkIpwXqvbyj2zVwHnkNuYy8PkQu7+hcQM70poBOXqRUnBKkN/oPxp1z+wD30VMAAAAASUVORK5CYII=", "drag-m.gif"] + }, + + 'dragging1-2': function() { + return ["dragBadge1-2-m.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAC80lEQVRIx7XWX2jWdRTH8demubKpNIug/xeLMiWoiyCUtSn0V6Sx/lwWYtGNBZlsVIh0tQ2TIYYlQcYIo24EFxWGXdQa1kqSRpFr5uY298z5PM+e7Vmzp3272O+3trn/rgMHzpcffN+/8znnd86P+dvSyBffNnL7J7wIdeysYyd8yrZS7rhiQD0VWygroyLHyCEOtHLuDJ2HOJBjZCNPP0XpYbbOW5oSbkXBUQ5nyTXTkiYMEVKRDxHShJP8liXXwBEs38Btc5W2oJGGZppO0TpAGCBcnMYz0fOTnG6mqZEvUDATIH8dN+Lm9/goEPpnAEz2fkIgvMvHuGU9N2HJlKR9VHcz1EZfah6Q2FOENvq6GHqHmukyWrKG8g4GswuAxD5IaCe7hnLkTyCspbCJgy20djKcvALQRUIHw6f441s+KGLleFbeZl6Ni7uQy/si743OCUIFVROyepDiXdT+Trp/gZALESQRxb+S2U3deu4aA73O9iTDmUWAnCd0R3E3f1fy2phsWPUCezNR51wpJAZtYx+KIgav8GyCVGaBkJ5xkE7COULXaPdlqnh+TLr7eOwtjp5hsH+RIC0MvcFnD01q8/zHeSlBrn8OkFiqGNI1DtIexW2MPMH2CXPvOlYd48gvDJ4nl5ylfWeCtBNO808T2QaOrRyt0Zhdi7X38HI72cFZit4TSRVDOiLA2f9k++tudizjXhReNoPq2JscTftScpbOmgpylvAzl9oIe9g/3ay7qoSHV/NcDZ/H07t3CkgsVQz5M4pThGqO38DWTTw53brIi0t2nG++48IJEmlCcgZIfP6a3i/pa+AHXB/dlz/TXlrxAOVLebSer9KE70n1RBJ2Rp6IYI2kOwn1NGHz/Txj0iCddgFGb3P1Dt7cQm0ZtQOE/TT/SPInUtWc6CNs4u1HqKliD1bMJZPL9lM0PgrvpGQ3B1FSyYeV1GPDLt5fR2nUWasX4zfsmtHPzbICigsojgpdhOX+J8sb1zhztn8BfjnSqGxySMwAAAAASUVORK5CYII=", "dragBadge1-2-m.gif"]; + }, + + 'dragging3': function() { + return ["dragBadge3-m.png", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAaCAYAAADfcP5FAAAED0lEQVRIx7XXWWzVRRTH8Q+byF4FRBQRDa4YICIhJpDIC0QjAZQI0QcVVIwPilFR0CjERAUNgWgxKqtRA4Kg0QdAYlzKVlCQtmyFQoGyFbqXktY6PtxpLaW3QIFJTs7cuZP//3vP+Z0zc7n40Qot47x19C2TrLVyJcZobl/IeJjPO2/wTC+uT2dJT675gJdmMKkXKeksuZnuk5nwBdNgIU+N4M5LBvmKscMZMpKnq6ieT+oh8newezlfVxGWsDib3L0cXMqiKsIKvsli12FOzSf1H6pHM344Q77jOTS7GI6WA7kJbVazoozKP9lZQqggFBKKCWWEguiLotVdK457KwglhK3sKaZyDavQYQA9LzSlbTezehPrt5NTRiglnIovq7HCej7ZWgHhZAQsImwjJ40N6/gV7RoDaX4P3dBjEctC/GUFl2CnouUTThCOx/kZwjx+aEPPQfSoUxBnj1Q+PELFfgqKLgPMyXowxwhHomVSmE3FLOYki1CLvozJ40z5FYA5GkEOEw7Fz7up7MO4GKH/hX4tHTcwL5N9R6gsvIwwxxqAySUcIOykaj25v8T2cZZ+RvNa4SXqJhlMXoQ5GGH2E3Lq+Ed5G81raQZx2zQ+2kNJyWWAOX4BMHuj30L5VFL70acW6E0mFVFZeplhDkerSdF+wj5CNmFPQkMhO2H/vsJbNTpqhpTnmVMae0VTYOqLNy/q5WAjMLvi2ng+R+daYb/M4/kUlzQBpqGyPh/MroSgQ1b0GZS/zsTalN3HQ9P56QCni5vY8JLB5JwHZhNnJrPmAcaiRW2VjeCFfKpLmthj8qIdrKOZfY3AZEb/N2EEr8ZzLZGyXqSs5cdMyo9TXXiJPaYmRXvr6WVnBMmItoXqtVQsZ117rqvbh9qhT18mJevUF9tj9sbI1MDsiBHJiFHJIqRTdTdTcC86nHN2fMKcgsRDz+rWDcEcaSLM9giURuVfhPeZn+wsu+p+hnfmydmsrTntTzbQY+rC5EbLSZKm+pHJiHtnkNaNZ4fyGNo2BFRzuHX5nY1/cGIL+SWxNx1L0vByG6ik3VEvO6JmtkefSVhF4UoKVpKF7vG9zRu7F3UayCN4cCm/FSfEV5QfU1dzfTgaKymvTqoO1Gl2u2JksiPIz5RmEb5kWwtG9ecJ9Q7UpBe1SN1mKtOG8d4oPi4nzCY9g7I0TsxhyynCLDanUbCRwplszkvs27qKExs5PZ30fYnS/mwoM6cwN4KcNzLn3I8SNxPtBjBsKp9i8Lt8O5HUroxZxtYOjHyRBZNY3IGRS9nWhXETmDuN7zF4Mgv68XCspK5Jb4gXMdoiBa27cMfV3IKUG+mPjq3p3Zre6HRDooRTOnFrZ+6K/9M6o70rNJolmV/o942O/wCWgGlL1BxZXgAAAABJRU5ErkJggg==", "dragBadge3-m.gif"]; + } + + }, + + templates: { + 'table-header-cell': function() { + return new uki.theme.Template( + '
${data}
'); + }, + + 'table-cell': function() { + return new uki.theme.Template( + '
${data}
'); + } + }, + + styles: { + 'fontFamily': function() { + return 'Lucida Grande,Arial,Helvetica,sans-serif' + } + }, + + doms: { + 'resizer': function(height) { + var template = new uki.theme.Template('position:absolute;width:7px;top:0;right:-4px;height:${height}px;cursor:col-resize;cursor:ew-resize;z-index:101;background:url(' + uki.theme.imageSrc('x') + ')'), + node = uki.createElement('div', template.render({height:height})); + + if (!node.style.cursor || window.opera) node.style.cursor = 'e-resize'; + return node; + }, + 'splitPane-vertical': function(params) { + var commonVerticalStyle = 'cursor:row-resize;cursor:ns-resize;z-index:200;overflow:hidden;', + handle = params.handleWidth == 1 ? + uki.createElement('div', + defaultCss + 'width:100%;height:5px;margin-top:-2px;' + + commonVerticalStyle + 'background: url(' + uki.theme.imageSrc('x') + ')', + '
') : + uki.createElement('div', + defaultCss + 'width:100%;height:' + params.handleWidth + 'px;' + commonVerticalStyle + + 'background: url(' + uki.theme.imageSrc('splitPane-v-bg') + ') 50% 50% repeat-x;', + '
' + ); + if (!handle.style.cursor || window.opera) handle.style.cursor = 'n-resize'; + return handle; + }, + + 'splitPane-horizontal': function(params) { + var commonHorizontalStyle = 'cursor:col-resize;cursor:ew-resize;z-index:200;overflow:hidden;', + handle = params.handleWidth == 1 ? + uki.createElement('div', + defaultCss + 'height:100%;width:5px;margin-left:-2px;' + + commonHorizontalStyle + 'background: url(' + uki.theme.imageSrc('x') + ')', + '
') : + uki.createElement('div', + defaultCss + 'height:100%;width:' + (params.handleWidth - 2) + 'px;' + + 'border: 1px solid #CCC;' + commonHorizontalStyle + + 'background: url(' + uki.theme.imageSrc('splitPane-horizontal') + ') 50% 50% no-repeat;'); + if (!handle.style.cursor || window.opera) handle.style.cursor = 'e-resize'; + return handle; + + } + } + }); + + function u(url) { + return uki_mail_app.theme.imagePath + url; + } + + function shadowData() { + var prefix = "shadow/large-"; + return { + c: [u(prefix + "c.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAYAAABXuSs3AAACzklEQVRo3t2a63KiUBCEPYCX1U2Ixvd/Qm/kYjRBWd2aTjW950CS3fyYtaprULl8p2kGAcMg/QqR6SDTsXk/8moi041Mx+bt3WAKVDVIDOQj0ArcROCbFHzoAGbYTICzLwygC/jc8T62bGccFDKLKLUXeH2625sIpCo2mBa8bkiBWbkpo5oaQMrxFPCJ6ikxkNYAQg90Tiqk5h0DiDmeAoZqqTqIFrxuSB0uSENTQVUHkHJdnVbgN6qYrmkQ6n7U6VygRwY6Eg1pHiyDdcQcx0YZGLCvInxWyx44q+Nwi6Hh8Ng0kTqieTQ2QcCbSDzeCPB40UHqUfYAlvu9Lu0aDD0i0B+iiQnup1wfdLgNdw+mFxEG8CrwZziuB6JCT00zqQyfcn3Q4TZD7y96lrqPwL9HJkiLKygecPcK+tN0Y3VG348lMlnC8bNE5EjuXmGfLnq0+mSf4fujuh6kM8DtCUHfmG6pMry63uc4u83QDwaO+kjwB3U9SD45InD61lSS4PzU4GNxUXCNyYvFAU5XpAcTnOfI/AFeiNuIxhX0TgT3pxKXoge8lpjsyeWdqKLosOs1wIcEzgck3L6Czk0Le1/ad7O/BH826MpgNxdtTTtynQ/UFngh4DNym6HvbfqO4oKcfwYc+UZMdga7FviKss7gdbB45NJNAA637wl8QXFBzsfSz7vAccLZ00EJt9dU4TofpOgup0AbLKSbICYAZiEu3NM/6zh6NmKyFm0oLtxdWo5z/8ZJpiTwpYDPxfGvgsPxrUCvCLyik9J7P1dw7igAB+zStDDw8h+BVwa+MeAVDQDg3FmS4NxR5gTN9TvA1wS9opxrZ+kFL6mbLEnfDb6iqGzJ8f8f3F1UXB6cLtuhyxOQy1O+2x9Zbn/Wur2QcHvp5vZi2e3tCbc3hNzegnN709P1bWaXN/bdPkpx/fDK9eNCtw9oXT8Sd/MnhF+iLpLibpmRrgAAAABJRU5ErkJggg==", u(prefix + "c.gif")], + v: [u(prefix + "v.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAECAYAAADxjg1nAAAAWklEQVQYGdXBWwpAQAAAwLEeSUqy9z/hSkpSnh9OsTMFGlSo0aJDjwEjJkREREwYMaBHhxY1KpQIKPxePLhx4cSBHRtWLJiRkJAwY8GKDTsOnLiCTAWZCjL1AeihFg5/1kytAAAAAElFTkSuQmCC", u(prefix + "v.gif")], + h: [u(prefix + "h.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAuCAYAAAAPxrguAAAAe0lEQVQoz5XSWwtAQBCG4XEMOST+/y8kOYScKRe8WzZbc7FPX7PNtLaIuPI49l0vUBIewT/LuO/7BRETMRMpExkh/w9KD+WVhBASAu20jnZjFsEkGAQh7ISNsBIWwkwYCT2hI9SEilASiv+g9KgEH6ZhomVi0E47fW7sAEmnGr/QVlzBAAAAAElFTkSuQmCC", u(prefix + "h.gif")], + m: [u(prefix + "m.png"), "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAEUlEQVQIHWNgYGD4i4ZJFQAAAkoP0RsgosoAAAAASUVORK5CYII=", u(prefix + "m.gif"), true] + }; + }; + + uki_mail_app.theme.backgrounds['toolbar-button-full-disabled'] = uki_mail_app.theme.backgrounds['toolbar-button-full-normal']; + uki_mail_app.theme.backgrounds['toolbar-button-left-disabled'] = uki_mail_app.theme.backgrounds['toolbar-button-left-normal']; + uki_mail_app.theme.backgrounds['toolbar-button-right-disabled'] = uki_mail_app.theme.backgrounds['toolbar-button-right-normal']; + uki_mail_app.theme.backgrounds['toolbar-button-mid-disabled'] = uki_mail_app.theme.backgrounds['toolbar-button-mid-normal']; + + uki.theme.register(uki_mail_app.theme); +})(); diff --git a/uki_mail_app/view.js b/uki_mail_app/view.js new file mode 100644 index 0000000..d2fb795 --- /dev/null +++ b/uki_mail_app/view.js @@ -0,0 +1,3 @@ +include('../uki_mail_app.js'); + +uki_mail_app.view = {}; \ No newline at end of file diff --git a/uki_mail_app/view/folders.js b/uki_mail_app/view/folders.js new file mode 100644 index 0000000..ea92afe --- /dev/null +++ b/uki_mail_app/view/folders.js @@ -0,0 +1,118 @@ +include('../view.js'); +include('../../frameworks/uki/uki-more/more/view/treeList.js'); + +uki_mail_app.view.folders = {}; + +uki.view.declare('uki_mail_app.view.Folders', uki.view.VFlow, function(Base) { + + this._setup = function() { + Base._setup.call(this); + }; + + this.appendChild = function(c) { + this._bindListEvents(c); + return Base.appendChild.apply(this, arguments); + }; + + this.insertBefore = function(c) { + this._bindListEvents(c); + return Base.insertBefore.apply(this, arguments); + }; + + this.removeChild = function(c) { + this._unbindListEvents(c); + return Base.removeChild.apply(this, arguments); + }; + + this.relayout = function() { + this._listUpdate(); + }; + + this.dropPreview = function() { + if (!this._dropPreview) { + this._dropPreview = uki({ view: 'Box', rect: '0 0 100 100', anchors: 'left top', style: {zIndex: 100}, background: 'theme(drop-preview)', visible: false }) + .appendTo(this)[0]; + } + return this._dropPreview; + }; + + this.selectedRow = uki.newProp('_selectedRow', function(v) { + var p = -1; + uki('[data]', this).each(function(i, list) { + if ( (p = uki.inArray(v, list.data())) > -1 ) { + list.selectedIndex(p); + return false; + } + }) + }); + + this._createDom = function() { + Base._createDom.call(this); + uki.dom.bind(doc.body, 'mouseup drop dragend', uki.proxy(this._dragend, this)); + uki.dom.bind(doc.body, 'dragenter', uki.proxy(this._dragenter, this)); + uki.dom.bind(this.dom(), 'dragenter', uki.proxy(this._dragenter, this)); + }; + + this._unbindListEvents = function() { + c.unbind('open close', this._listUpdate); + c.unbind('open close', this._selectionUpdate); + }; + + this._bindListEvents = function(c) { + if (c.selectedIndexes) { + c.bind('open close', uki.proxy(this._listUpdate, this)); + c.bind('selection', uki.proxy(this._selectionUpdate, this, c)); + c.bind('dragover', uki.proxy(this._dragover, this, c)); + } + }; + + this._dragend = function(e) { + this.dropPreview().visible(false); + }; + + this._dragenter = function(e) { + if (!uki.dom.contains(this.dom(), e.target)) { + this.dropPreview().visible(false); + } + }; + + this._dragover = function(c, e) { + var o = uki.dom.offset(c.dom()), + y = e.pageY - o.y, + p = y / c.rowHeight() << 0, + row = c.listData()[p]; + + if (this._lastRow == row) return; + this._lastRow = row; + if (row && row.dropTarget) { + this.dropPreview().visible(true); + this.dropPreview().rect(new uki.geometry.Rect(0, c.rect().y + p * c.rowHeight() - 1, c.rect().width, c.rowHeight())).layout(); + } else { + this.dropPreview().visible(false); + } + if (this._openTimeout) { + clearTimeout(this._openTimeout); + this._openTimeout = null; + } + if (row && row.children) { + this._openTimeout = setTimeout(uki.proxy(function() { + c.open(p); + }, this), 1000); + } + }; + + this._selectionUpdate = function(c, e) { + uki('[selectedIndexes]', this).each(function(i, list) { + if (list != c) list.clearSelection(); + }); + this._selectedRow = c.selectedRows()[0]; + this.trigger('selection'); + }; + + this._listUpdate = function() { + uki('[selectedIndexes]', this).resizeToContents('height'); + this.resizeToContents('height'); + this.layout(); + if (this.parent()) this.parent().layout(); + }; +}); \ No newline at end of file diff --git a/uki_mail_app/view/folders/render.js b/uki_mail_app/view/folders/render.js new file mode 100644 index 0000000..f599685 --- /dev/null +++ b/uki_mail_app/view/folders/render.js @@ -0,0 +1,54 @@ +include('../folders.js'); +include('../../../frameworks/uki/uki-more/more/view/treeList/render.js'); + +uki_mail_app.view.folders.Render = uki.newClass(uki.more.view.treeList.Render, function(Base) { + this._parentTemplate = new uki.theme.Template( + '
' + + '
${text}' + + '
' + ); + + this._leafTemplate = new uki.theme.Template( + '
${text}
' + ); + + this.initStyles = function() { + this.classPrefix = 'treeList-' + uki.guid++; + var style = new uki.theme.Template( + '.${classPrefix}-row { color: #333; position:relative; padding:4px 3px 3px 22px; white-space: nowrap } ' + + '.${classPrefix}-toggle { overflow: hidden; position:absolute; left:-14px; top:5px; width: 10px; height:9px; } ' + + '.${classPrefix}-toggle i { display: block; position:absolute; left: 0; top: 0; width:20px; height:18px; background: url(${imageSrc});} ' + + '.${classPrefix}-selected-blured .${classPrefix}-row { color: #FFF; } ' + + '.${classPrefix}-selected-blured i { left: -10px; } ' + + '.${classPrefix}-selected-blured { background: url(${bgSrc}); } ' + + '.${classPrefix}-opened i { top: -9px; }' + ).render({ + classPrefix: this.classPrefix, + imageSrc: uki.theme.imageSrc('arrows'), + bgSrc: uki.theme.imageSrc('tree-selected-bg') + }); + uki.dom.createStylesheet(style); + }; + + this.render = function(row, rect, i) { + this.classPrefix || this.initStyles(); + var text = row.data; + if (row.children && row.children.length) { + return this._parentTemplate.render({ + text: text, + indent: row.__indent*18 + 35, + classPrefix: this.classPrefix, + icon: uki.theme.imageSrc(row.icon), + opened: row.__opened ? 'opened' : '' + }); + } else { + return this._leafTemplate.render({ + text: text, + indent: row.__indent*18 + 20 + (row.leaf ? 0 : 15), + icon: uki.theme.imageSrc(row.icon), + classPrefix: this.classPrefix + }); + } + }; + +}); \ No newline at end of file diff --git a/uki_mail_app/view/main.js b/uki_mail_app/view/main.js new file mode 100644 index 0000000..106d04c --- /dev/null +++ b/uki_mail_app/view/main.js @@ -0,0 +1,161 @@ +include('../view.js'); + +uki.view.declare('uki_mail_app.view.Main', uki.view.Box, function(Base) { + + this._createDom = function() { + Base._createDom.call(this); + uki([ + // toolbar + { view: 'Box', rect: '1000 68', anchors: 'left top right', + background: 'theme(panel-dark)', childViews: [ + { view: 'Label', rect: '0 0 1000 20', anchors: 'left top right', + style: { textAlign: 'center', textShadow: '0 1px 0px rgba(255,255,255,0.8)', color: '#000' }, + text: 'Inbox – voloko@gmail.com (100 messages)' }, + + { view: 'uki_mail_app.view.Toolbar', rect: '0 25 1000 23', anchors: 'left top right', + algorithm: 'ResizeLast', + childViews: [ + { view: 'Box', rect: '60 23', anchors: 'left top', textSelectable: false, childViews: + { view: 'uki_mail_app.view.ToolbarButton', label: 'Get Mail', rect: '10 0 41 23', anchors: 'left top', backgroundPrefix: 'toolbar-button-full-', + icon: uki.theme.imageSrc('toolbar-get-mail') } + }, + + { view: 'Box', rect: '100 23', anchors: 'left top', spacer: true, minSize: '30 0', prefferedWidth: '190' }, + + { view: 'uki_mail_app.view.Toolbar', rect: '750 23', anchors: 'left top right', textSelectable: false, + childViews: [ + { view: 'Box', rect: '110 23', anchors: 'left top', + childViews: [ + { view: 'uki_mail_app.view.ToolbarButton', rect: '0 0 55 23', label: 'Delete', anchors: 'left top', backgroundPrefix: 'toolbar-button-left-', + icon: uki.theme.imageSrc('toolbar-delete'), togglable: 1, not_empty: 1 }, + { view: 'uki_mail_app.view.ToolbarButton', rect: '54 0 55 23', label: 'Junk', anchors: 'left top', backgroundPrefix: 'toolbar-button-right-', + icon: uki.theme.imageSrc('toolbar-junk'), togglable: 1, not_empty: 1 } + ] }, + + { view: 'Box', rect: '351 23', anchors: 'left top', + childViews: [ + { view: 'uki_mail_app.view.ToolbarButton', rect: '10 0 55 23', label: 'Reply', anchors: 'left top', backgroundPrefix: 'toolbar-button-left-', + icon: uki.theme.imageSrc('toolbar-reply'), togglable: 1, not_empty: 1, not_multy: 1 }, + { view: 'uki_mail_app.view.ToolbarButton', rect: '64 0 55 23', label: 'Reply All', anchors: 'left top', backgroundPrefix: 'toolbar-button-mid-', + icon: uki.theme.imageSrc('toolbar-reply-all'), togglable: 1, not_empty: 1, not_multy: 1 }, + { view: 'uki_mail_app.view.ToolbarButton', rect: '118 0 55 23', label: 'Forward', anchors: 'left top', backgroundPrefix: 'toolbar-button-right-', + icon: uki.theme.imageSrc('toolbar-forward'), togglable: 1, not_empty: 1 }, + + { view: 'uki_mail_app.view.ToolbarButton', label: 'Redirect', rect: '190 0 41 23', anchors: 'left top', backgroundPrefix: 'toolbar-button-full-', + icon: uki.theme.imageSrc('toolbar-redirect'), togglable: 1, not_empty: 1, not_multy: 1 }, + + { view: 'uki_mail_app.view.ToolbarButton', label: 'New Message', rect: '260 0 41 23', anchors: 'left top', backgroundPrefix: 'toolbar-button-full-', + icon: uki.theme.imageSrc('toolbar-new') } + ] }, + + + { view: 'Box', rect: '100 23', anchors: 'left top', spacer: true, minSize: '10 0' }, + + { view: 'Box', rect: '200 23', anchors: 'top right', childViews: + { view: 'uki_mail_app.view.SearchField', rect: '10 1 170 22', anchors: 'right top', backgroundPrefix: 'search-', label: 'Search' } + } + + ]} + ]} + ] }, + // content + { view: 'HSplitPane', rect: '0 68 1000 932', anchors: 'left top right bottom', + handlePosition: 250, minLeft: 150, handleWidth: 1, + leftChildViews: [ + + // left folder tree + { view: 'ScrollPane', rect: '250 910', anchors: 'left top right bottom', background: '#DDE4EB', childViews: [ + { view: 'uki_mail_app.view.Folders', rect: '250 200', anchors: 'left top right', + style: { fontSize: '12px' }, + childViews: [ + { view: 'Label', rect: '250 20', anchors: 'left top right', text: 'MAILBOXES', inset: '6 0 2 9', + background: 'theme(tree-list-header)' }, + { view: 'uki.more.view.TreeList', rect: '250 10', anchors: 'left top right', + style: {fontSize: '11px', fontFamily: uki.theme.style('fontFamily'), lineHeight: '11px' }, + render: new uki_mail_app.view.folders.Render(), + data: [ + { data: 'Inbox', icon: 'mb-inbox', name: 'INBOX', dropTarget: true }, + { data: 'Drafts', icon: 'mb-drafts', name: 'Drafts', dropTarget: true }, + { data: 'Sent', icon: 'mb-sent', name: 'Sent', dropTarget: true }, + { data: 'Trash', icon: 'mb-trash', name: 'Trash', dropTarget: true } + ], rowHeight: 20, focusable: false, textSelectable: false }, + { view: 'Label', rect: '250 23', anchors: 'left top right', text: 'MAIL@UKIJS.ORG', inset: '6 0 2 9', + background: 'theme(tree-list-header)' }, + { view: 'uki.more.view.TreeList', rect: '250 10', anchors: 'left top right', + style: {fontSize: '11px', fontFamily: uki.theme.style('fontFamily'), lineHeight: '11px' }, + render: new uki_mail_app.view.folders.Render(), + data: [ + { data: '[Gmail]', icon: 'mb-folder-empty', __opened: true, + children: [ + { data: 'All Mail', icon: 'mb-folder', name: 'gmail/all_mail', dropTarget: true }, + { data: 'Drafts', icon: 'mb-folder', name: 'gmail/drafts', dropTarget: true }, + { data: 'Sent Mail', icon: 'mb-folder', name: 'gmail/sent_mail', dropTarget: true }, + { data: 'Spam', icon: 'mb-folder', name: 'gmail/spam', dropTarget: true }, + { data: 'Starred', icon: 'mb-folder', name: 'gmail/starred', dropTarget: true }, + { data: 'Trash', icon: 'mb-folder', name: 'gmail/trash', dropTarget: true } + ]}, + { data: 'Label 1', icon: 'mb-folder', name: 'gmail/label1', dropTarget: true }, + { data: 'Label 2', icon: 'mb-folder', name: 'gmail/label2', dropTarget: true }, + { data: 'Label 3', icon: 'mb-folder', name: 'gmail/label3', dropTarget: true } + ], rowHeight: 20, focusable: false, textSelectable: false } + ] } + ] }, + // left toolbar + { view: 'Box', rect: '0 910 250 22', anchors: 'left bottom right', background: 'theme(panel-light)' } + ], + + rightChildViews: [ + { view: 'VSplitPane', rect: '749 932', anchors: 'left top right bottom', handleWidth: 10, + handlePosition: 200, minTop: 100, + topChildViews: [ + // message list + { view: 'uki_mail_app.view.MessageTable', rect: '749 200', anchors: 'left top right bottom', + style: { fontSize: '12px', lineHeight: '12px' }, multiselect: true } + ], + bottomChildViews: [ + // message + // { view: 'Box', rect: '100 10 100 100', anchors: 'left top', background: '#CCC', id: 'drop' } + ] + } + ] + } + ]).appendTo(this); + + uki('Folders', this)[0].relayout(); + uki('Folders', this)[0].selectedRow(uki('Folders TreeList:eq(0)', this).data()[0]); + uki('Folders', this).parent().attr('textSelectable', false); + var spacer = uki('Toolbar [spacer]', this)[0]; + + uki('HSplitPane', this).bind('handleMove', function(e) { + spacer.prefferedWidth = this.handlePosition() - 60; + spacer.parent()._resizeChildViews(); + spacer.parent().layout(); + }); + + var toolbar = uki('Toolbar:eq(0)', this); + uki('MessageTable List', this).bind('selection', function() { + var indexes = this.selectedIndexes(); + uki('[togglable]', toolbar).disabled(false); + if (indexes.length == 0) uki('[not_empty]', toolbar).disabled(true); + if (indexes.length > 1) uki('[not_multy]', toolbar).disabled(true); + }).trigger('selection'); + + // uki('#drop', this).dragover(function(e) { + // e.preventDefault(); + // e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed.indexOf('copy') > -1 ? 'copy' : 'move'; + // console.log([e.dataTransfer.effectAllowed, e.dataTransfer.dropEffect]); + // }).drop(function(e) { + // e.preventDefault(); + // console.log(e.dataTransfer); + // for (var i=0; i < e.dataTransfer.files.length; i++) { + // console.log(e.dataTransfer.files[i]); + // } + // for (var i=0; i < e.dataTransfer.types.length; i++) { + // console.log(e.dataTransfer.types[i]); + // console.log(e.dataTransfer.getData(e.dataTransfer.types[i])); + // }; + // }); + + }; + +}); \ No newline at end of file diff --git a/uki_mail_app/view/messageTable.js b/uki_mail_app/view/messageTable.js new file mode 100644 index 0000000..8159eeb --- /dev/null +++ b/uki_mail_app/view/messageTable.js @@ -0,0 +1,188 @@ +include('../view.js'); + +uki_mail_app.view.messageTable = {}; + +uki.view.declare('uki_mail_app.view.MessageTable', uki.view.Table, function(Base) { + this._setup = function() { + Base._setup.call(this); + this._initialWidths = []; + this._rowHeight = 17; + }; + + function unreadFormatter (v) { + return v*1 ? '
' : ''; + } + + var attachmentTemplate = new uki.theme.Template( + '
${count}' + ); + function attachmentFormatter (v) { + v*=1; + if (!v) return ''; + return attachmentTemplate.render({ + count: v + }); + } + + this._createDom = function() { + Base._createDom.call(this); + this._scrollPane.scrollableH(false); + this._scrollPane.scrollV(true); + this._header.background('theme(table-header)'); + this._list.render(new uki_mail_app.view.messageTable.Render(this)); + this._list.draggable(true).bind('dragstart', uki.proxy(this._dragstart, this)); + + // preload images + this.getDragImage(2).left(-999).attachTo(window); + this.getDragImage(100).left(-999).attachTo(window); + + + this.columns([ + // { view: 'uki_mail_app.view.messageTable.Column', label: '', width: 19, minWidth: 19 }, + { view: 'uki_mail_app.view.messageTable.Column', label: '', inset: '0 0', width: 19, minWidth: 19, + formatter: unreadFormatter }, + { view: 'uki_mail_app.view.messageTable.Column', label: 'From', width: 200, minWidth: 150, resizable: true }, + { view: 'uki_mail_app.view.messageTable.Column', label: 'Subject', width: 300, minWidth: 150, resizable: true }, + { view: 'uki_mail_app.view.messageTable.DateColumn', label: 'Date Recieved', width: 200, minWidth: 70, resizable: true, + name: 'date', table: this }, + { view: 'uki_mail_app.view.messageTable.Column', label: '', + width: 30, minWidth: 30, formatter: attachmentFormatter, resizable: true } + ]); + + var classPrefix = this._list.render().classPrefix, + style = new uki.theme.Template( + '.${classPrefix}-selected { background: url(' + uki.theme.imageSrc('list-selected-row') + '); color: #FFF } ' + + '.${classPrefix}-selected-blured { background: url(' + uki.theme.imageSrc('list-selected-blured-row') + '); } ' + + '.${classPrefix}-list .unread { background: url(' + uki.theme.imageSrc('list-unread') + '); width: 16px; height: 16px; margin: -1px 0 0 1px; } ' + + '.${classPrefix}-list .attachment { background: url(' + uki.theme.imageSrc('list-attachment') + '); width: 16px; height: 16px; margin: -1px 4px 0 1px; float: left; } ' + + '.${classPrefix}-selected .unread { background: url(' + uki.theme.imageSrc('list-unread-selected') + '); } ' + + '.${classPrefix}-selected .attachment { background: url(' + uki.theme.imageSrc('list-attachment-selected') + '); } ' + ).render({ + classPrefix: classPrefix + }); + this._list.className(classPrefix + '-list'); + uki.dom.createStylesheet(style); + }; + + this.columns = uki.newProp('_columns', function(c) { + for (var i = 0, column; i < this._columns.length; i++) { + this._columns[i].unbind(); + } + this._columns = uki.build(c); + this._initialTotalWidth = 0; + this._initialWidths = []; + this._resizableCols = 0; + for (i = 0; i < this._columns.length; i++) { + column = this._columns[i]; + this._initialWidths[i] = column.width(); + if (column.resizable()) this._resizableCols++; + column.position(i); + // column.table(this); + this._initialTotalWidth += column.width(); + this._columns[i].bind('beforeResize', uki.proxy(this._beforeColumnResize, this, i)); + }; + this._header.columns(this._columns); + this._fitWidth(); + }); + + this.relayout = function() { + this._list.relayout(); + }; + + this.getDragImage = function(count) { + return uki({ view: 'uki_mail_app.view.messageTable.Drag', rect: '100 32', anchors: 'left top', count: count || this._list.selectedIndexes().length }); + }; + + this._dragstart = function(e) { + e.dataTransfer.effectAllowed = 'all'; + e.dataTransfer.setData('text/uri-list', 'xxx.html'); + e.dataTransfer.setData('text/html', 'yuppy 111'); + e.dataTransfer.setData('text/plain', 'yuppy 111'); + e.dataTransfer.setData('File', 'yuppy 111'); + e.dataTransfer.setDragImage(this.getDragImage(), 10, 10); + }; + + var processingWidths = false; + this._beforeColumnResize = function(position, e) { + if (processingWidths) return; + processingWidths = true; + var diff = e.newWidth - e.oldWidth, + nextColumn, iw, change, i, step; + if (diff > 0) { + i = this._columns.length; + step = -1; + } else { + i = position; + step = 1; + } + do { + nextColumn = this._columns[i+=step]; + if (i == position || !nextColumn) break; + if (!nextColumn.resizable()) continue; + nextColumn.maxWidth(0); + iw = nextColumn.width(); + nextColumn.width(iw - diff); + change = iw - nextColumn.width(); + diff -= change; + } while (diff); + + this._fixMaxWidths(); + this._recalcInitialWidths(); + processingWidths = false; + }; + + this._recalcInitialWidths = function() { + this._initialTotalWidth = 0; + for (var i = 0; i < this._columns.length; i++) { + this._initialWidths[i] = this._columns[i].width(); + this._initialTotalWidth += this._initialWidths[i]; + } + }; + + this._layoutDom = function(rect) { + Base._layoutDom.call(this, rect); + this._fitWidth(); + }; + + this._fixMaxWidths = function() { + var fixed = 0, offsets = [0], width = this.rect().width - uki.view.ScrollPane.initScrollWidth(); + for (var i=1; i < this._columns.length; i++) { + offsets[i] = offsets[i-1] + this._columns[i-1].width(); + }; + for (i = this._columns.length - 1; i >= 0; i--) { + var column = this._columns[i]; + if (column.resizable()) { + column.maxWidth(width - offsets[i] - fixed); + } + fixed += column.minWidth() || column.width(); + } + }; + + this._fitWidth = function() { + processingWidths = true; + var scroll = uki.view.ScrollPane.initScrollWidth(), + diff = this.rect().width - scroll - this._initialTotalWidth; + var dw, change, resizable = this._resizableCols, widths = [].concat(this._initialWidths); + while (diff && resizable) { + var colsToGo = resizable; + for (var i = 0, column; colsToGo > 0 && i < this._columns.length; i++) { + column = this._columns[i]; + column.maxWidth(0); + if (column.resizable()) { + dw = diff / colsToGo << 0; + column.width(widths[i] + dw); + change = column.width() - widths[i]; + if (change != dw) resizable--; + widths[i] += change; + diff -= change; + colsToGo--; + } + } + }; + this._updateTotalWidth(); + this._fixMaxWidths(); + processingWidths = false; + }; +}); + +include('messageTable/render.js'); diff --git a/uki_mail_app/view/messageTable/column.js b/uki_mail_app/view/messageTable/column.js new file mode 100644 index 0000000..b4bc7ea --- /dev/null +++ b/uki_mail_app/view/messageTable/column.js @@ -0,0 +1,5 @@ +include('../messageTable.js'); + +uki.view.declare('uki_mail_app.view.messageTable.Column', uki.view.table.Column, function(Base) { + this._inset = new uki.geometry.Inset(1, 3); +}); \ No newline at end of file diff --git a/uki_mail_app/view/messageTable/dateColumn.js b/uki_mail_app/view/messageTable/dateColumn.js new file mode 100644 index 0000000..3a99610 --- /dev/null +++ b/uki_mail_app/view/messageTable/dateColumn.js @@ -0,0 +1,95 @@ +include('column.js'); + +uki.view.declare('uki_mail_app.view.messageTable.DateColumn', uki_mail_app.view.messageTable.Column, function(Base) { + + this.table = uki.newProp('_table'); + + this.width = function(v) { + if (v === undefined) return Base.width.call(this); + Base.width.call(this, v); + this._updateColumnFormatting(); + return this; + }; + + this._updateColumnFormatting = function(position) { + var formatter; + var w = this.width(); + if (w > 190) { + formatter = dateFormatterLongest; + } else if (w > 150){ + formatter = dateFormatterLong; + } else if (w > 130){ + formatter = dateFormatterMid; + } else if (w > 100){ + formatter = dateFormatterShort; + } else { + formatter = dateFormatterShortest; + } + if (this.formatter() != formatter) { + this.formatter(formatter); + if (this.table()) { + this.table().redrawColumn( uki.inArray(this, this.table().columns()) ); + } + } + }; + + + var mNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + mShortNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + + function formatTime (d) { + return (d.getHours() % 12) + ':' + add0(d.getMinutes()) + ' ' + (d.getHours() > 12 ? 'PM' : 'AM'); + } + + function formatDateLongest (d) { + return mNames[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); + } + + function formatDateLong (d) { + return mShortNames[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear(); + } + + function formatYear (y) { + if (y > 1900) y -= 1900; + if (y > 100) y -= 100; + return add0(y); + } + + function add0 (x) { + return x < 10 ? '0' + x : x; + } + + function formatDateShort (d) { + return add0(d.getMonth() + 1) + '/' + add0(d.getDate()) + '/' + formatYear(d.getYear()); + } + + function dateFormatterLongest (v) { + var d = new Date(); + d.setTime(v * 1000); + return '
' + formatTime(d) + '
' + formatDateLongest(d); + } + + function dateFormatterLong (v) { + var d = new Date(); + d.setTime(v * 1000); + return '
' + formatTime(d) + '
' + formatDateLong(d); + } + + function dateFormatterMid (v) { + var d = new Date(); + d.setTime(v * 1000); + return '
' + formatTime(d) + '
' + formatDateShort(d); + } + + function dateFormatterShort (v) { + var d = new Date(); + d.setTime(v * 1000); + return formatDateLong(d); + } + + function dateFormatterShortest (v) { + var d = new Date(); + d.setTime(v * 1000); + return formatDateShort(d); + } +}); \ No newline at end of file diff --git a/uki_mail_app/view/messageTable/drag.js b/uki_mail_app/view/messageTable/drag.js new file mode 100644 index 0000000..812ce47 --- /dev/null +++ b/uki_mail_app/view/messageTable/drag.js @@ -0,0 +1,24 @@ +include('../messageTable.js'); + +uki.view.declare('uki_mail_app.view.messageTable.Drag', uki.view.Container, function(Base) { + + this._createDom = function() { + Base._createDom.call(this); + uki([ + { view: 'Image', rect: '32 32', anchors: 'left top', src: uki.theme.imageSrc('dragging') }, + { view: 'Label', rect: '24 16 26 26', anchors: 'left top', + background: 'url(' + uki.theme.imageSrc('dragging1-2') + ')', text: '1', + style: { textAlign: 'center', color: '#FFF', fontSize: '11px', fontFamily: uki.theme.style('fontFamily') } + } + ]).appendTo(this); + }; + + this.count = uki.newProp('_count', function(v) { + this._count = v; + uki('Label', this)[0] + .text(v) + .background('url(' + uki.theme.imageSrc(v < 100 ? 'dragging1-2' : 'dragging3') + ')') + .width(v < 100 ? 26 : 36) + .layout(); + }) +}); \ No newline at end of file diff --git a/uki_mail_app/view/messageTable/render.js b/uki_mail_app/view/messageTable/render.js new file mode 100644 index 0000000..5bfb232 --- /dev/null +++ b/uki_mail_app/view/messageTable/render.js @@ -0,0 +1,10 @@ +uki_mail_app.view.messageTable.Render = uki.newClass(uki.view.table.Render, function(Base) { + this.init = function(table) { + this.classPrefix = 'messageTable-' + uki.guid++; + Base.init.call(this, table); + } + + this.setSelected = function(container, data, state, focus) { + container.className = !state ? '' : focus ? this.classPrefix + '-selected' : this.classPrefix + '-selected-blured'; + } +}); \ No newline at end of file diff --git a/uki_mail_app/view/searchField.js b/uki_mail_app/view/searchField.js new file mode 100644 index 0000000..a851fe8 --- /dev/null +++ b/uki_mail_app/view/searchField.js @@ -0,0 +1,33 @@ +include('../view.js'); + +uki.view.declare('uki_mail_app.view.SearchField', uki.view.Container, function(Base) { + + uki.each(['html', 'backgroundPrefix', 'inset'], function(i, name) { + uki.delegateProp(this, name, '_input'); + }, this); + + this.label = function(html) { + if (html === undefined) return this._label.innerHTML; + this._label.html(html).resizeToContents('width').layout(); + return this; + }; + + this.disabled = function(value) { + if (value === undefined) return this._button.disabled(); + this._input.disabled(value); + this._label.style('color', value ? '#666' : '#000') + return this; + }; + + + this._createDom = function() { + Base._createDom.call(this); + var rect = this.rect(); + this._input = uki({ view: 'TextField', rect: rect.clone().normalize(), anchors: 'left top right bottom' })[0]; + this._label = uki({ view: 'Label', rect: new uki.geometry.Rect((rect.width-10)/2, rect.height+2,0,11), anchors: 'bottom', + style: { fontSize: '11px', textShadow: '0 1px 0px rgba(255,255,255,0.5)', fontFamily: 'Lucida Grande,Arial,Helvetica,sans-serif' } })[0]; + this.appendChild(this._input); + this.appendChild(this._label); + }; + +}); \ No newline at end of file diff --git a/uki_mail_app/view/toolbar.js b/uki_mail_app/view/toolbar.js new file mode 100644 index 0000000..682b9be --- /dev/null +++ b/uki_mail_app/view/toolbar.js @@ -0,0 +1,51 @@ +include('../view.js'); + +uki.view.declare('uki_mail_app.view.Toolbar', uki.view.HFlow, function(Base) { + var Rect = uki.geometry.Rect; + + this.initWidths = function() { + var minWidth = 0; + for (var i=0, childViews = this.childViews(); i < childViews.length; i++) { + minWidth += childViews[i].minSize().width || childViews[i].rect().width; + if (uki.attr(childViews[i], 'spacer')) this._spacer = childViews[i]; + } + this._minSize = new uki.geometry.Size( minWidth, this.minSize.height ); + }; + + this.algorithm = function(name) { + this._resizeChildViews = this['_algorithm' + name]; + }; + + this._resizeChildViews = this._algorithmResizeSpacer = function() { + if (this._contentChanged) this.initWidths(); + // expected rect + var diff = this.rect().width - this.minSize().width; + this._spacer.width(this._spacer.minSize().width + diff); + Base._resizeChildViews.call(this); + this._rect.width = this.childViews()[ this.childViews().length - 1].maxX(); + }; + + this._algorithmResizeLast = function() { + var lastChild = this.childViews()[this.childViews().length - 1 ]; + + if (this._contentChanged) { + lastChild.resizeToContents('width'); + this.initWidths(); + } + // expected rect + var prefferedWidth = uki.attr(this._spacer, 'prefferedWidth')*1, + minWidth = this._spacer.minSize().width, + flex = prefferedWidth - minWidth, + diff = this.rect().width - this.minSize().width - flex; + if (diff < 0) { + this._spacer.width(prefferedWidth + diff); + lastChild.width(lastChild.minSize().width); + } else { + this._spacer.width(prefferedWidth); + lastChild.width(lastChild.minSize().width + diff); + } + Base._resizeChildViews.call(this); + this._rect.width = lastChild.maxX(); + }; + +}); \ No newline at end of file diff --git a/uki_mail_app/view/toolbarButton.js b/uki_mail_app/view/toolbarButton.js new file mode 100644 index 0000000..c2b2159 --- /dev/null +++ b/uki_mail_app/view/toolbarButton.js @@ -0,0 +1,54 @@ +include('../view.js'); + +uki.view.declare('uki_mail_app.view.ToolbarButton', uki.view.Container, function(Base) { + + uki.each(['html', 'backgroundPrefix', 'inset'], function(i, name) { + uki.delegateProp(this, name, '_button'); + }, this) + + this.label = function(html) { + if (html === undefined) return this._label.innerHTML; + this._label.html(html).resizeToContents('width').layout(); + return this; + }; + + this.icon = uki.newProp('_icon', function(icon) { + this._icon = icon; + this._button.html( + '' + ); + }); + + this.disabled = function(value) { + if (value === undefined) return this._button.disabled(); + this._button.disabled(value); + this._label.style('color', value ? '#666' : '#000'); + this._button._label.style.opacity = value ? '0.5' : ''; + return this; + }; + + this._createDom = function() { + Base._createDom.call(this); + var rect = this.rect(); + this._button = uki({ view: 'Button', rect: rect.clone().normalize(), anchors: 'left top right bottom', inset: '0', focusable: false })[0]; + this._label = uki({ view: 'Label', rect: new uki.geometry.Rect(rect.width/2, rect.height+2,0,11), anchors: 'bottom', + style: { fontSize: '11px', textShadow: '0 1px 0px rgba(255,255,255,0.5)', fontFamily: 'Lucida Grande,Arial,Helvetica,sans-serif' } })[0]; + this.appendChild(this._button); + this.appendChild(this._label); + + this._label.bind('mousedown', uki.proxy(this._passEvent, this)); + this._label.bind('mouseleave', uki.proxy(this._passEvent, this)); + this._button._updateBg = uki.proxy(this._updateBg, this); + }; + + this._passEvent = function(e) { + this._button.trigger(e.type, e); + }; + + this._updateBg = function() { + uki.view.Button.prototype._updateBg.call(this._button); + this.style('zIndex', this._button._down || this._button._over ? 200 : 100); + }; + + +}); \ No newline at end of file