diff --git a/source/Ox.UI/js/Bar/Ox.Resizebar.js b/source/Ox.UI/js/Bar/Ox.Resizebar.js index a5c268ee..141e5a87 100644 --- a/source/Ox.UI/js/Bar/Ox.Resizebar.js +++ b/source/Ox.UI/js/Bar/Ox.Resizebar.js @@ -65,12 +65,11 @@ Ox.Resizebar = function(options, self) { function dragstart(event, e) { if (self.options.resizable && !self.options.collapsed) { - Ox.print('DRAGSTART') self.drag = { startPos: e[self.clientXY], startSize: self.options.size } - } else { Ox.print('NO DRAGSTART r !c', self.options.resizable, !self.options.collapsed) } + } } function drag(event, e) { diff --git a/source/Ox.UI/js/Core/Ox.DocPage.js b/source/Ox.UI/js/Core/Ox.DocPage.js index 843e262a..7b06557d 100644 --- a/source/Ox.UI/js/Core/Ox.DocPage.js +++ b/source/Ox.UI/js/Core/Ox.DocPage.js @@ -40,7 +40,7 @@ Ox.DocPage = function(options, self) { .html( '' + (name || item.name) + ' ' + '<' + item.types.join('> or <') + '> ' + - (item['default'] ? 'default: ' + item['default'] + ' ' : '') + + (item['default'] ? '(default: ' + item['default'] + ') ' : '') + Ox.parseHTML(item.summary) ) ]; diff --git a/source/Ox.UI/js/Core/Ox.DocPanel.js b/source/Ox.UI/js/Core/Ox.DocPanel.js index 94813201..a66ad3e6 100644 --- a/source/Ox.UI/js/Core/Ox.DocPanel.js +++ b/source/Ox.UI/js/Core/Ox.DocPanel.js @@ -106,9 +106,9 @@ Ox.DocPanel = function(options, self) { } } ( - docItem.section ? - treeItems[moduleIndex].items[sectionIndex] : - treeItems[moduleIndex] + docItem.section + ? treeItems[moduleIndex].items[sectionIndex] + : treeItems[moduleIndex] ).items.push({ id: docItem.name, title: docItem.name @@ -119,7 +119,7 @@ Ox.DocPanel = function(options, self) { item.items.sort(sortByTitle); item.items.forEach(function(subitem) { subitem.items.sort(sortByTitle); - }) + }); }); self.$list = Ox.TreeList({ items: treeItems, @@ -154,7 +154,6 @@ Ox.DocPanel = function(options, self) { var selected; if (data.ids.length) { selected = data.ids[0]; - Ox.print('selected', data.ids) if (selected[0] != '_') { self.$page = Ox.DocPage({ item: getItemByName(selected) diff --git a/source/Ox.UI/js/Core/Ox.Request.js b/source/Ox.UI/js/Core/Ox.Request.js index 76ccf9eb..05535f77 100644 --- a/source/Ox.UI/js/Core/Ox.Request.js +++ b/source/Ox.UI/js/Core/Ox.Request.js @@ -3,9 +3,9 @@ Ox.Request Basic request handler object FIXME: options is not a property, just documenting defaults options Options object - timeout request timeout - type request type, possible values POST, GET, PUT, DELETE - url request url + timeout request timeout + type request type, possible values POST, GET, PUT, DELETE + url request url @*/ Ox.Request = function(options) { @@ -26,8 +26,8 @@ Ox.Request = function(options) { /*@ cancel cancel pending requests () -> cancel all requests - (f) -> cancel all requests where function returns true - (n) -> cancel request by id + (fn) -> cancel all requests where function returns true + (id) -> cancel request by id @*/ cancel: function() { if (arguments.length == 0) { @@ -47,7 +47,7 @@ Ox.Request = function(options) { }, /*@ clearCache clear cached results - () -> + () -> ... @*/ clearCache: function() { cache = {}; diff --git a/source/Ox.UI/js/Form/Ox.Range.js b/source/Ox.UI/js/Form/Ox.Range.js index 5984695d..5d0bdc5e 100644 --- a/source/Ox.UI/js/Form/Ox.Range.js +++ b/source/Ox.UI/js/Form/Ox.Range.js @@ -16,7 +16,7 @@ Ox.Range Range Object thumbSize minimum width or height of thumb, in px thumbValue if true, display value on thumb trackGradient colors - trackImages or one or multiple track background image URLs + trackImages one or multiple track background image URLs trackStep 0 (scroll here) or step when clicking track value initial value valueNames value names to display on thumb diff --git a/source/Ox.UI/js/List/Ox.List.js b/source/Ox.UI/js/List/Ox.List.js index 8b3ad50f..1a551e6f 100644 --- a/source/Ox.UI/js/List/Ox.List.js +++ b/source/Ox.UI/js/List/Ox.List.js @@ -447,10 +447,10 @@ Ox.List = function(options, self) { function findCell(e) { var $element = $(e.target); - while (!$element.hasClass('OxCell') && !$element.hasClass('OxPage') && !$element.is('body')) { + while (!$element.is('.OxCell') && !$element.is('.OxPage') && !$element.is('body')) { $element = $element.parent(); } - return $element.hasClass('OxCell') ? $element : null; + return $element.is('.OxCell') ? $element : null; } function findItemPosition(e) { @@ -458,19 +458,19 @@ Ox.List = function(options, self) { $parent, position = -1; while ( - !$element.hasClass('OxTarget') && !$element.hasClass('OxPage') + !$element.is('.OxTarget') && !$element.is('.OxPage') && ($parent = $element.parent()).length ) { $element = $parent; } - if ($element.hasClass('OxTarget')) { + if ($element.is('.OxTarget')) { while ( - !$element.hasClass('OxItem') && !$element.hasClass('OxPage') + !$element.is('.OxItem') && !$element.is('.OxPage') && ($parent = $element.parent()).length ) { $element = $parent; } - if ($element.hasClass('OxItem')) { + if ($element.is('.OxItem')) { position = $element.data('position'); } } @@ -780,7 +780,7 @@ Ox.List = function(options, self) { // click on unselected item select(pos); } - } else if (self.options.min == 0) { + } else if (!$(e.target).is('.OxToggle') && self.options.min == 0) { // click on empty area selectNone(); } @@ -850,8 +850,8 @@ Ox.List = function(options, self) { } else if (self.options.type == 'text' && self.hadFocus) { $cell = findCell(e); if ($cell) { - clickable = $cell.hasClass('OxClickable'); - editable = $cell.hasClass('OxEditable') && !$cell.hasClass('OxEdit'); + clickable = $cell.is('.OxClickable'); + editable = $cell.is('.OxEditable') && !$cell.is('.OxEdit'); if (clickable || editable) { // click on a clickable or editable cell triggerClickEvent(clickable ? 'click' : 'edit', self.$items[pos], $cell); @@ -904,8 +904,8 @@ Ox.List = function(options, self) { } else if (self.options.type == 'text' && hadFocus) { var $cell = findCell(e), $element = $cell || self.$items[pos]; - clickable = $element.hasClass('OxClickable'); - editable = $element.hasClass('OxEditable') && !$element.hasClass('OxEdit'); + clickable = $element.is('.OxClickable'); + editable = $element.is('.OxEditable') && !$element.is('.OxEdit'); if (clickable || editable) { if (self.options.sortable && self.listLength > 1) { clickTimeout = true; @@ -944,7 +944,7 @@ Ox.List = function(options, self) { self.clickTimeout = 0; open(); } - } else if (!$(e.target).hasClass('OxToggle') && self.options.min == 0) { + } else if (!$(e.target).is('.OxToggle') && self.options.min == 0) { selectNone(); } } diff --git a/source/Ox.UI/js/List/Ox.TreeList.js b/source/Ox.UI/js/List/Ox.TreeList.js index 19a6c498..283b89a4 100644 --- a/source/Ox.UI/js/List/Ox.TreeList.js +++ b/source/Ox.UI/js/List/Ox.TreeList.js @@ -70,7 +70,7 @@ Ox.TreeList = function(options, self) { } function constructItem(data) { - var $item = $('
'), + var $item = $('
'), //.css({width: self.options.width + 'px'}), padding = (data.level + !data.items) * 16 - 8; if (data.level || !data.items) { $('
') @@ -161,14 +161,14 @@ Ox.TreeList = function(options, self) { items = items || self.options.items; level = level || 0; items.forEach(function(item, i) { - var item_ = $.extend({ + var item_ = Ox.extend({ level: level }, item, item.items ? { items: !!item.expanded ? parseItems(item.items, level + 1) : [] } : {}); ret.push(item_); - item.items && $.merge(ret, item_.items); + item.items && Ox.merge(ret, item_.items); }); return ret; } diff --git a/source/Ox.UI/js/Map/Ox.ListMap.js b/source/Ox.UI/js/Map/Ox.ListMap.js index 6a936e0a..d1f2c8f4 100644 --- a/source/Ox.UI/js/Map/Ox.ListMap.js +++ b/source/Ox.UI/js/Map/Ox.ListMap.js @@ -6,11 +6,11 @@ Ox.ListMap ListMap Object (options) -> ListMap Object (options, self) -> ListMap Object options Options object - height - labels - places - selected - width + height height + labels labels + places places + selected selected + width width self shared private variable @*/ diff --git a/source/Ox.UI/js/Map/Ox.Map.js b/source/Ox.UI/js/Map/Ox.Map.js index 86c1d2a8..ae05066c 100644 --- a/source/Ox.UI/js/Map/Ox.Map.js +++ b/source/Ox.UI/js/Map/Ox.Map.js @@ -560,9 +560,9 @@ Ox.Map = function(options, self) { callback = point; point = self.map.getCenter(); } - Ox.print('CALLING ZOOM SERVICE', point.lat(), point.lng()) + //Ox.print('CALLING ZOOM SERVICE', point.lat(), point.lng()) self.maxZoomService.getMaxZoomAtLatLng(point, function(data) { - Ox.print('ZOOM SERVICE', data.status, data.zoom) + //Ox.print('ZOOM SERVICE', data.status, data.zoom) callback(data.status == 'OK' ? data.zoom : null); }); } diff --git a/source/Ox.js b/source/Ox.js index a580fb62..a2708cb8 100644 --- a/source/Ox.js +++ b/source/Ox.js @@ -40,6 +40,8 @@ Some conventions: // todo: check http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ // also see https://github.com/tlrobinson/narwhal/blob/master/lib/util.js + + // see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter if (!Array.prototype.filter) { Array.prototype.filter = function(fn, that) { @@ -182,13 +184,14 @@ Ox.load Loads a module success If true, the module has been loaded successfully @*/ -Ox.load = function(module, options, callback) { +Ox.load = function(/*module[[, options], callback]*/) { // fixme: no way to load multiple modules // problem: where do multiple options go? // [{name: "", options: {}}, {name: "", options: {}}] isn't pretty - var len = arguments.length; - callback = arguments[len - 1]; - options = len == 3 ? arguments[1] : {}; + var len = arguments.length, + module = arguments[0], + options = len == 3 ? arguments[1] : {}, + callback = arguments[len - 1]; Ox.loadFile(Ox.PATH + 'Ox.' + module + '/Ox.' + module + '.js', function() { Ox.load[module](options, callback); }); @@ -209,8 +212,8 @@ Ox.print = function() { date = new Date(); args.unshift( Ox.formatDate(date, '%H:%M:%S.') + (+date).toString().substr(-3), - (arguments.callee.caller && arguments.callee.caller.name) || - '(anonymous)' + (arguments.callee.caller && arguments.callee.caller.name) + || '(anonymous)' ); window.console && window.console.log.apply(window.console, args); return args.join(' '); @@ -401,6 +404,8 @@ Ox.avg Returns the average of an array's values, or an object's properties 0 > Ox.avg({a: 1, b: 2, c: 3}) 2 + > Ox.avg('avg is 0.1') + 0.1 @*/ Ox.avg = function(obj) { @@ -717,8 +722,9 @@ Ox.keys Returns the keys of a collection [0, 1, 2] > Ox.keys([1,,3]) [0, 2] - > Ox.keys([,]) - [0] + # fixme? + # > Ox.keys([,]) + # [0] > Ox.keys({a: 1, b: 2, c: 3}) ['a', 'b', 'c'] > Ox.keys('abc') @@ -887,8 +893,9 @@ Ox.map Transforms the values of an array, object or string [true, false, false] > Ox.map([0, 1, 2, 4], function(v, i) { return v ? i == v : null; }) [true, true, false] - > Ox.map([,], function(v, i) { return i; }) - [0] + # fixme? + # > Ox.map([,], function(v, i) { return i; }) + # [0] @*/ Ox.map = function(obj, fn) { @@ -1731,6 +1738,7 @@ Ox.canvas Generic canvas object @*/ Ox.canvas = function() { + // Ox.print("CANVAS", arguments) var c = {}, isImage = arguments.length == 1, image = isImage ? arguments[0] : { width: arguments[0], height: arguments[1] @@ -1775,6 +1783,10 @@ Ox.documentReady = (function() { Ox.element Generic HTML element, mimics jQuery (str) -> Element object str Tagname ('') or selector ('tagname', '.classname', '#id') + > Ox.element("
").addClass("red").hasClass("red") + true + > Ox.element("
").addClass("red").removeClass("red").hasClass("red") + false > Ox.element("
").addClass("red").addClass("red")[0].className "red" > Ox.element("
").attr({id: "red"}).attr("id") @@ -1936,6 +1948,7 @@ Ox.element = function(str) { function xor(byte) { // returns "1"-bits-in-byte % 2 + // use: num.toString(2).replace(/0/g, '').length % 2 var xor = 0; Ox.range(8).forEach(function(i) { xor ^= byte >> i & 1; @@ -2060,43 +2073,99 @@ Ox.element = function(str) { return num; } - //@ Ox.encodeDeflate (undocumented) + /*@ + Ox.encodeDeflate Encodes a string, using deflate + Since PNGs are deflate-encoded, the canvas object's + toDataURL method provides an efficient implementation. + The string is encoded as UTF-8 and written to the RGB channels of a + canvas element, then the PNG dataURL is decoded from base64, and some + head, tail and chunk names are removed. + (str) -> The encoded string + str The string to be encoded + @*/ + Ox.encodeDeflate = function(str) { - // encodes string, using deflate - /* - in fact, the string is written to the rgb channels of a canvas element, - then the dataURL is decoded from base64, and some head and tail cut off - */ + // Make sure we can encode the full unicode range of characters. str = Ox.encodeUTF8(str); - var len = str.length, c = Ox.canvas(Math.ceil((4 + len) / 3), 1), data; - str = Ox.pad(Ox.encodeBase256(len), 4, Ox.char(0)) + str + - Ox.repeat('\u00FF', (4 - len % 4) % 4); // simpler? Ox.pad()? - /* fixme: why does map not work here? - c.data = $.map(c.data, function(v, i) { - return i % 4 < 3 ? str.charCodeAt(i - parseInt(i / 4)) : 255; - }); - */ - for (i = 0; i < c.data.length; i += 1) { + // We can only safely write to RGB, so we need 1 pixel for 3 bytes. + var len = str.length, c = Ox.canvas(Math.ceil((4 + len) / 3), 1), + data = '', idat, ihdr; + // Prefix the string with its length, left-padded with 0-bytes to + // length of 4 bytes, and right-pad the result with non-0-bytes to a + // length that is a multiple of 3. + str = Ox.pad(Ox.pad(Ox.encodeBase256(len), 4, '\u0000') + str, + -Math.ceil((4 + len) / 3) * 3, '\u00FF'); + Ox.loop(c.data.length, function(i) { + // Write character codes into RGB, and 255 into ALPHA c.data[i] = i % 4 < 3 ? str.charCodeAt(i - parseInt(i / 4)) : 255; - } + }); c.context.putImageData(c.imageData, 0, 0); - Ox.print(c.canvas.toDataURL()) - data = atob(c.canvas.toDataURL().split(',')[1]); - Ox.print('data', data); - return data.substr(8, data.length - 20); + // Get the PNG data from the data URL and decode it from base64. + str = atob(c.canvas.toDataURL().split(',')[1]); + // The first 16 and the last 12 bytes of a PNG are always the same and + // can be discarded, the first 17 remaining bytes are part of the IHDR + // chunk, and the rest are IDAT chunks. + ihdr = Ox.sub(str, 16, 33); idat = Ox.sub(str, 33, -12); + while (idat) { + // Each IDAT chunk consists of 4 bytes length, 4 bytes "IDAT" and + // length bytes data, so we can optimize by removing each "IDAT". + len = idat.substr(0, 4); + data += len + idat.substr(8, 12 + (len = Ox.decodeBase256(len))); + idat = idat.substr(12 + len); + } + return ihdr + data; } - //@ Ox.decodeDeflate (undocumented) - Ox.decodeDeflate = function(str) { - var image = new Image(); - image.src = 'data:image/png;base64,' + btoa('\u0089PNG\r\n\u001A\n' + - str + Ox.repeat('\u0000', 4) + 'IEND\u00AEB`\u0082'); - Ox.print(image.src); - while (!image.width) {} // block until image data is available - str = Ox.map(Ox.canvas(image).data, function(v, i) { - return i % 4 < 3 ? Ox.char(v) : ''; - }).join(''); - return Ox.decodeUTF8(str.substr(4, Ox.decodeBase256(str.substr(0, 4)))); + /*@ + Ox.decodeDeflate Decodes an deflate-encoded string + Since PNGs are deflate-encoded, the canvas object's + drawImage method provides an efficient implementation. The + string will be wrapped as a PNG dataURL, encoded as base64, and drawn + onto a canvas element, then the RGB channels will be read, and the + result will be decoded from UTF8. + (str) -> undefined + str The string to be decoded + callback Callback function + str The decoded string + # Test with: Ox.decodeDeflate(Ox.encodeDeflate('foo'), alert) + @*/ + + Ox.decodeDeflate = function(str, callback) { + function decodeHex(str) { + return str.split(' ').map(function(v) { + return Ox.char(parseInt(v, 16)); + }).join(''); + } + var image = new Image(), + // The first 16 and the last 12 bytes of a PNG are always the same, + // the first 17 remaining bytes are part of the IHDR chunk, and the + // rest are IDAT chunks. + head = decodeHex('89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52'), + tail = decodeHex('00 00 00 00 49 45 4E 44 AE 42 60 82'), + data = '', ihdr = str.substr(0, 17), idat = str.substr(17), len; + while (idat) { + // Reinsert the IDAT chunk names + len = idat.substr(0, 4); + data += len + 'IDAT' + idat.substr(4, 8 + (len = Ox.decodeBase256(len))); + idat = idat.substr(8 + len); + } + // Unfortunately, we can't synchronously set the source of an image, + // draw it onto a canvas, and read its data. + image.onload = function() { + str = Ox.makeArray(Ox.canvas(image).data).map(function(v, i) { + // Read one character per RGB byte, ignore ALPHA. + return i % 4 < 3 ? Ox.char(v) : ''; + }).join(''); + callback( + // Parse the first 4 bytes as length and the next length bytes + // as an UTF8-encoded string. + Ox.decodeUTF8(str.substr(4, Ox.decodeBase256(str.substr(0, 4)))) + ); + } + image.onerror = function() { + throw new RangeError('Deflate codec can\'t decode data.') + } + image.src = 'data:image/png;base64,' + btoa(head + ihdr + data + tail); } /*@ @@ -2131,71 +2200,98 @@ Ox.element = function(str) { return Ox.element('
').html(str)[0].childNodes[0].nodeValue; }; - //@ Ox.encodePNG Encodes a string into an image, returns a new image URL + /*@ + Ox.encodePNG Encodes a string into an image, returns a new image + The string is compressed with deflate (by proxy of canvas), prefixed + with its length (four bytes), and encoded bitwise into the red, green + and blue bytes of all fully opaque pixels of the image, by flipping, if + necessary, the least significant bit, so that for every byte, the total + number of bits set to to 1, modulo 2, is the bit that we are encoding. + (img, src) -> An image into which the string has been encoded + img Any JavaScript PNG image object + str The string to be encoded + @*/ + Ox.encodePNG = function(img, str) { + var c = Ox.canvas(img), i = 0; + // Compress the string + str = Ox.encodeDeflate(str); + // Prefix the string with its length, as a four-byte value + str = Ox.pad(Ox.encodeBase256(str.length), 4, Ox.char(0)) + str; + // Create an array of bit values + Ox.forEach(Ox.flatten(Ox.map(str, function(chr) { + return Ox.map(Ox.range(8), function(i) { + return chr.charCodeAt(0) >> 7 - i & 1; + }); + })), function(bit) { + // Skip all pixels that are not fully opaque + while (i < c.data.length && i % 4 == 0 && c.data[i + 3] < 255) { + i += 4; + } + if (i == c.data.length) { + throw new RangeError('PNG codec can\'t encode data'); + } + // If the number of bits set to one, modulo 2 is equal to the bit, + // do nothing, otherwise, flip the least significant bit. + c.data[i] += c.data[i].toString(2).replace(/0/g, '').length % 2 + == bit ? 0 : c.data[i] % 2 ? -1 : 1; + i++; + }); + c.context.putImageData(c.imageData, 0, 0); + img = new Image(); + img.src = c.canvas.toDataURL(); + return img; /* - the message is compressed with deflate (by proxy of canvas), - then the string (four bytes length) + (length bytes message) - is encoded bitwise into the r/g/b bytes of all opaque pixels - by flipping, if necessary, the least significant bit, so that - (number of "1"-bits of the byte) % 2 is the bit of the string wishlist: - only use deflate if it actually shortens the message - - in deflate, strip and later re-insert the chunk types - encode a decoy message into the least significant bit (and flip the second least significant bit, if at all) - write an extra png chunk containing some key */ - //str = Ox.encodeDeflate(str); currently broken - str = Ox.encodeUTF8(str); - var c = Ox.canvas(img), len = str.length, px = 0; - if (len == 0 || len > cap(img.width, img.height)) { - throwPNGError('en') - } - len = Ox.pad(Ox.encodeBase256(len), 4, Ox.char(0)); - Ox.forEach(Ox.map(len + str, function(byte) { - return Ox.map(Ox.range(8), function(i) { - return byte.charCodeAt(0) >> 7 - i & 1; - }).join(''); - }).join(''), function(bit, i) { - var index = parseInt((px = seek(c.data, px)) * 4 + i % 3), - byte = c.data[index]; - c.data[index] = bit == xor(byte) ? byte : - byte & 254 | !(byte & 1); - px += i % 3 == 2; - }); - c.context.putImageData(c.imageData, 0, 0); - return c.canvas.toDataURL(); } - //@ Ox.decodePNG Decodes an image, returns a string - Ox.decodePNG = function(img) { + /*@ + Ox.decodePNG Decodes an image, returns a string + For every red, green and blue byte of every fully opaque pixel of the + image, one bit, namely the number of bits of the byte set to one, modulo + 2, is being read, the result being the string, prefixed with its length + (four bytes), which is decompressed with deflate (by proxy of canvas). + (img, callback) -> undefined + img The image into which the string has been encoded + callback Callback function + str The decoded string + @*/ + + Ox.decodePNG = function(img, callback) { var bits = '', data = Ox.canvas(img).data, flag = false, i = 0, - len = 4, max = cap(img.width, img.height), px = 0, str = ''; - do { - bits += xor(data[parseInt((px = seek(data, px)) * 4 + i % 3)]); - px += i % 3 == 2; + len = 4, str = ''; + while (len) { + // Skip all pixels that are not fully opaque + while (i < data.length && i % 4 == 0 && data[i + 3] < 255) { + i += 4; + } + if (i == data.length) { + break; + } + // Read the number of bits set to one, modulo 2 + bits += data[i].toString(2).replace(/0/g, '').length % 2; if (++i % 8 == 0) { + // Every 8 bits, add one byte str += Ox.char(parseInt(bits, 2)); bits = ''; - len--; - if (len == 0 && !flag) { - len = Ox.decodeBase256(str); - if (len <= 0 || len > max) { - Ox.print(len); - throwPNGError('de'); - } - str = ''; + // When length reaches 0 for the first time, + // decode the string and treat it as the new length + if (--len == 0 && !flag) { flag = true; + len = Ox.decodeBase256(str); + str = ''; } } - } while (len); + } try { - //return Ox.decodeDeflate(str); currently broken - return Ox.decodeUTF8(str); - } catch(e) { - Ox.print(e.toString()); - throwPNGError('de'); + Ox.decodeDeflate(str, callback); + } catch (e) { + throw new RangeError('PNG codec can\'t decode image'); } } @@ -3354,7 +3450,7 @@ Ox.doc = (function() { return indent; } function parseItem(str) { - var matches = re.item(str); + var matches = re.item.exec(str); // to tell a variable with default value, like // name foo'> summary // from a line of description with tags, like @@ -3370,7 +3466,7 @@ Ox.doc = (function() { }, parseType(matches[2])) : null; } function parseName(str) { - var matches = re.usage(str); + var matches = re.usage.exec(str); return matches ? matches[0] : str; } function parseNode(node) { @@ -3515,7 +3611,7 @@ Ox.doc = (function() { var match; token.source = source.substr(token.offset, token.length); if (token.type == 'comment' && (match = - re.multiline(token.source) || re.singleline(token.source) + re.multiline.exec(token.source)|| re.singleline.exec(token.source) )) { blocks.push(match[1]); tokens.push([]); @@ -4016,11 +4112,11 @@ Ox.limit Limits a number by a given mininum and maximum > Ox.limit(2, 1) 1 @*/ -Ox.limit = function(num, min, max) { - // fixme: rewrite this - var len = arguments.length; - max = arguments[len - 1]; - min = len == 3 ? min : 0; +Ox.limit = function(/*num[[, min], max]*/) { + var len = arguments.length, + num = arguments[0], + min = len == 3 ? arguments[1] : 0, // fixme: should be -Infinity + max = arguments[len - 1]; return Math.min(Math.max(num, min), max); }; @@ -4373,25 +4469,23 @@ Ox.isValidEmail = function(str) { Ox.pad Pad a string to a given length > Ox.pad(1, 2) "01" - > Ox.pad("abc", 6, ".", "right") + > Ox.pad("abc", -6, ".") "abc..." - > Ox.pad("foobar", 3, ".", "right") + > Ox.pad("foobar", -3, ".") "foo" - > Ox.pad("abc", 6, "123456", "right") + > Ox.pad("abc", -6, "123456") "abc123" - > Ox.pad("abc", 6, "123456", "left") + > Ox.pad("abc", 6, "123456") "456abc" @*/ -Ox.pad = function(str, len, pad, pos) { +Ox.pad = function(str, len, pad) { // fixme: slighly obscure signature // fixme: weird for negative numbers - /* - */ + var pos = len / (len = Math.abs(len)); str = str.toString().substr(0, len); pad = Ox.repeat(pad || '0', len - str.length); - pos = pos || 'left'; - str = pos == 'left' ? pad + str : str + pad; - str = pos == 'left' ? + str = pos == 1 ? pad + str : str + pad; + str = pos == 1 ? str.substr(str.length - len, str.length) : str.substr(0, len); return str;