fixing encoding functions (deflate, png)
This commit is contained in:
parent
318e2e95b2
commit
816993e1b9
10 changed files with 235 additions and 143 deletions
|
|
@ -65,12 +65,11 @@ Ox.Resizebar = function(options, self) {
|
||||||
|
|
||||||
function dragstart(event, e) {
|
function dragstart(event, e) {
|
||||||
if (self.options.resizable && !self.options.collapsed) {
|
if (self.options.resizable && !self.options.collapsed) {
|
||||||
Ox.print('DRAGSTART')
|
|
||||||
self.drag = {
|
self.drag = {
|
||||||
startPos: e[self.clientXY],
|
startPos: e[self.clientXY],
|
||||||
startSize: self.options.size
|
startSize: self.options.size
|
||||||
}
|
}
|
||||||
} else { Ox.print('NO DRAGSTART r !c', self.options.resizable, !self.options.collapsed) }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drag(event, e) {
|
function drag(event, e) {
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ Ox.DocPage = function(options, self) {
|
||||||
.html(
|
.html(
|
||||||
'<code><b>' + (name || item.name) + '</b> ' +
|
'<code><b>' + (name || item.name) + '</b> ' +
|
||||||
'<' + item.types.join('></code> or <code><') + '> </code>' +
|
'<' + item.types.join('></code> or <code><') + '> </code>' +
|
||||||
(item['default'] ? 'default: <code>' + item['default'] + ' </code>' : '') +
|
(item['default'] ? '(default: <code>' + item['default'] + '</code>) ' : '') +
|
||||||
Ox.parseHTML(item.summary)
|
Ox.parseHTML(item.summary)
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -106,9 +106,9 @@ Ox.DocPanel = function(options, self) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
docItem.section ?
|
docItem.section
|
||||||
treeItems[moduleIndex].items[sectionIndex] :
|
? treeItems[moduleIndex].items[sectionIndex]
|
||||||
treeItems[moduleIndex]
|
: treeItems[moduleIndex]
|
||||||
).items.push({
|
).items.push({
|
||||||
id: docItem.name,
|
id: docItem.name,
|
||||||
title: docItem.name
|
title: docItem.name
|
||||||
|
|
@ -119,7 +119,7 @@ Ox.DocPanel = function(options, self) {
|
||||||
item.items.sort(sortByTitle);
|
item.items.sort(sortByTitle);
|
||||||
item.items.forEach(function(subitem) {
|
item.items.forEach(function(subitem) {
|
||||||
subitem.items.sort(sortByTitle);
|
subitem.items.sort(sortByTitle);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
self.$list = Ox.TreeList({
|
self.$list = Ox.TreeList({
|
||||||
items: treeItems,
|
items: treeItems,
|
||||||
|
|
@ -154,7 +154,6 @@ Ox.DocPanel = function(options, self) {
|
||||||
var selected;
|
var selected;
|
||||||
if (data.ids.length) {
|
if (data.ids.length) {
|
||||||
selected = data.ids[0];
|
selected = data.ids[0];
|
||||||
Ox.print('selected', data.ids)
|
|
||||||
if (selected[0] != '_') {
|
if (selected[0] != '_') {
|
||||||
self.$page = Ox.DocPage({
|
self.$page = Ox.DocPage({
|
||||||
item: getItemByName(selected)
|
item: getItemByName(selected)
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
Ox.Request <o> Basic request handler object
|
Ox.Request <o> Basic request handler object
|
||||||
FIXME: options is not a property, just documenting defaults
|
FIXME: options is not a property, just documenting defaults
|
||||||
options <o> Options object
|
options <o> Options object
|
||||||
timeout <n|60000> request timeout
|
timeout <n|60000> request timeout
|
||||||
type <s|POST> request type, possible values POST, GET, PUT, DELETE
|
type <s|"POST"> request type, possible values POST, GET, PUT, DELETE
|
||||||
url <s> request url
|
url <s> request url
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
Ox.Request = function(options) {
|
Ox.Request = function(options) {
|
||||||
|
|
@ -26,8 +26,8 @@ Ox.Request = function(options) {
|
||||||
/*@
|
/*@
|
||||||
cancel <f> cancel pending requests
|
cancel <f> cancel pending requests
|
||||||
() -> <u> cancel all requests
|
() -> <u> cancel all requests
|
||||||
(f) -> <u> cancel all requests where function returns true
|
(fn) -> <u> cancel all requests where function returns true
|
||||||
(n) -> <u> cancel request by id
|
(id) -> <u> cancel request by id
|
||||||
@*/
|
@*/
|
||||||
cancel: function() {
|
cancel: function() {
|
||||||
if (arguments.length == 0) {
|
if (arguments.length == 0) {
|
||||||
|
|
@ -47,7 +47,7 @@ Ox.Request = function(options) {
|
||||||
},
|
},
|
||||||
/*@
|
/*@
|
||||||
clearCache <f> clear cached results
|
clearCache <f> clear cached results
|
||||||
() -> <u>
|
() -> <u> ...
|
||||||
@*/
|
@*/
|
||||||
clearCache: function() {
|
clearCache: function() {
|
||||||
cache = {};
|
cache = {};
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ Ox.Range <f:Ox.Element> Range Object
|
||||||
thumbSize <n> minimum width or height of thumb, in px
|
thumbSize <n> minimum width or height of thumb, in px
|
||||||
thumbValue <b> if true, display value on thumb
|
thumbValue <b> if true, display value on thumb
|
||||||
trackGradient <a> colors
|
trackGradient <a> colors
|
||||||
trackImages <s> or <a> one or multiple track background image URLs
|
trackImages <s|[s]> one or multiple track background image URLs
|
||||||
trackStep <n> 0 (scroll here) or step when clicking track
|
trackStep <n> 0 (scroll here) or step when clicking track
|
||||||
value <n> initial value
|
value <n> initial value
|
||||||
valueNames <a> value names to display on thumb
|
valueNames <a> value names to display on thumb
|
||||||
|
|
|
||||||
|
|
@ -447,10 +447,10 @@ Ox.List = function(options, self) {
|
||||||
|
|
||||||
function findCell(e) {
|
function findCell(e) {
|
||||||
var $element = $(e.target);
|
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();
|
$element = $element.parent();
|
||||||
}
|
}
|
||||||
return $element.hasClass('OxCell') ? $element : null;
|
return $element.is('.OxCell') ? $element : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findItemPosition(e) {
|
function findItemPosition(e) {
|
||||||
|
|
@ -458,19 +458,19 @@ Ox.List = function(options, self) {
|
||||||
$parent,
|
$parent,
|
||||||
position = -1;
|
position = -1;
|
||||||
while (
|
while (
|
||||||
!$element.hasClass('OxTarget') && !$element.hasClass('OxPage')
|
!$element.is('.OxTarget') && !$element.is('.OxPage')
|
||||||
&& ($parent = $element.parent()).length
|
&& ($parent = $element.parent()).length
|
||||||
) {
|
) {
|
||||||
$element = $parent;
|
$element = $parent;
|
||||||
}
|
}
|
||||||
if ($element.hasClass('OxTarget')) {
|
if ($element.is('.OxTarget')) {
|
||||||
while (
|
while (
|
||||||
!$element.hasClass('OxItem') && !$element.hasClass('OxPage')
|
!$element.is('.OxItem') && !$element.is('.OxPage')
|
||||||
&& ($parent = $element.parent()).length
|
&& ($parent = $element.parent()).length
|
||||||
) {
|
) {
|
||||||
$element = $parent;
|
$element = $parent;
|
||||||
}
|
}
|
||||||
if ($element.hasClass('OxItem')) {
|
if ($element.is('.OxItem')) {
|
||||||
position = $element.data('position');
|
position = $element.data('position');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -780,7 +780,7 @@ Ox.List = function(options, self) {
|
||||||
// click on unselected item
|
// click on unselected item
|
||||||
select(pos);
|
select(pos);
|
||||||
}
|
}
|
||||||
} else if (self.options.min == 0) {
|
} else if (!$(e.target).is('.OxToggle') && self.options.min == 0) {
|
||||||
// click on empty area
|
// click on empty area
|
||||||
selectNone();
|
selectNone();
|
||||||
}
|
}
|
||||||
|
|
@ -850,8 +850,8 @@ Ox.List = function(options, self) {
|
||||||
} else if (self.options.type == 'text' && self.hadFocus) {
|
} else if (self.options.type == 'text' && self.hadFocus) {
|
||||||
$cell = findCell(e);
|
$cell = findCell(e);
|
||||||
if ($cell) {
|
if ($cell) {
|
||||||
clickable = $cell.hasClass('OxClickable');
|
clickable = $cell.is('.OxClickable');
|
||||||
editable = $cell.hasClass('OxEditable') && !$cell.hasClass('OxEdit');
|
editable = $cell.is('.OxEditable') && !$cell.is('.OxEdit');
|
||||||
if (clickable || editable) {
|
if (clickable || editable) {
|
||||||
// click on a clickable or editable cell
|
// click on a clickable or editable cell
|
||||||
triggerClickEvent(clickable ? 'click' : 'edit', self.$items[pos], $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) {
|
} else if (self.options.type == 'text' && hadFocus) {
|
||||||
var $cell = findCell(e),
|
var $cell = findCell(e),
|
||||||
$element = $cell || self.$items[pos];
|
$element = $cell || self.$items[pos];
|
||||||
clickable = $element.hasClass('OxClickable');
|
clickable = $element.is('.OxClickable');
|
||||||
editable = $element.hasClass('OxEditable') && !$element.hasClass('OxEdit');
|
editable = $element.is('.OxEditable') && !$element.is('.OxEdit');
|
||||||
if (clickable || editable) {
|
if (clickable || editable) {
|
||||||
if (self.options.sortable && self.listLength > 1) {
|
if (self.options.sortable && self.listLength > 1) {
|
||||||
clickTimeout = true;
|
clickTimeout = true;
|
||||||
|
|
@ -944,7 +944,7 @@ Ox.List = function(options, self) {
|
||||||
self.clickTimeout = 0;
|
self.clickTimeout = 0;
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
} else if (!$(e.target).hasClass('OxToggle') && self.options.min == 0) {
|
} else if (!$(e.target).is('.OxToggle') && self.options.min == 0) {
|
||||||
selectNone();
|
selectNone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ Ox.TreeList = function(options, self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructItem(data) {
|
function constructItem(data) {
|
||||||
var $item = $('<div>'),
|
var $item = $('<div>'), //.css({width: self.options.width + 'px'}),
|
||||||
padding = (data.level + !data.items) * 16 - 8;
|
padding = (data.level + !data.items) * 16 - 8;
|
||||||
if (data.level || !data.items) {
|
if (data.level || !data.items) {
|
||||||
$('<div>')
|
$('<div>')
|
||||||
|
|
@ -161,14 +161,14 @@ Ox.TreeList = function(options, self) {
|
||||||
items = items || self.options.items;
|
items = items || self.options.items;
|
||||||
level = level || 0;
|
level = level || 0;
|
||||||
items.forEach(function(item, i) {
|
items.forEach(function(item, i) {
|
||||||
var item_ = $.extend({
|
var item_ = Ox.extend({
|
||||||
level: level
|
level: level
|
||||||
}, item, item.items ? {
|
}, item, item.items ? {
|
||||||
items: !!item.expanded ?
|
items: !!item.expanded ?
|
||||||
parseItems(item.items, level + 1) : []
|
parseItems(item.items, level + 1) : []
|
||||||
} : {});
|
} : {});
|
||||||
ret.push(item_);
|
ret.push(item_);
|
||||||
item.items && $.merge(ret, item_.items);
|
item.items && Ox.merge(ret, item_.items);
|
||||||
});
|
});
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ Ox.ListMap <f:Ox.Element> ListMap Object
|
||||||
(options) -> <f> ListMap Object
|
(options) -> <f> ListMap Object
|
||||||
(options, self) -> <f> ListMap Object
|
(options, self) -> <f> ListMap Object
|
||||||
options <o> Options object
|
options <o> Options object
|
||||||
height <n|256>
|
height <n|256> height
|
||||||
labels <b|false>
|
labels <b|false> labels
|
||||||
places <f|null>
|
places <f|null> places
|
||||||
selected <a|[]>
|
selected <a|[]> selected
|
||||||
width <n|256>
|
width <n|256> width
|
||||||
self <o> shared private variable
|
self <o> shared private variable
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -560,9 +560,9 @@ Ox.Map = function(options, self) {
|
||||||
callback = point;
|
callback = point;
|
||||||
point = self.map.getCenter();
|
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) {
|
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);
|
callback(data.status == 'OK' ? data.zoom : null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
306
source/Ox.js
306
source/Ox.js
|
|
@ -40,6 +40,8 @@ Some conventions:
|
||||||
// todo: check http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
|
// 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
|
// also see https://github.com/tlrobinson/narwhal/blob/master/lib/util.js
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
|
// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
|
||||||
if (!Array.prototype.filter) {
|
if (!Array.prototype.filter) {
|
||||||
Array.prototype.filter = function(fn, that) {
|
Array.prototype.filter = function(fn, that) {
|
||||||
|
|
@ -182,13 +184,14 @@ Ox.load <f> Loads a module
|
||||||
success <b> If true, the module has been loaded successfully
|
success <b> 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
|
// fixme: no way to load multiple modules
|
||||||
// problem: where do multiple options go?
|
// problem: where do multiple options go?
|
||||||
// [{name: "", options: {}}, {name: "", options: {}}] isn't pretty
|
// [{name: "", options: {}}, {name: "", options: {}}] isn't pretty
|
||||||
var len = arguments.length;
|
var len = arguments.length,
|
||||||
callback = arguments[len - 1];
|
module = arguments[0],
|
||||||
options = len == 3 ? arguments[1] : {};
|
options = len == 3 ? arguments[1] : {},
|
||||||
|
callback = arguments[len - 1];
|
||||||
Ox.loadFile(Ox.PATH + 'Ox.' + module + '/Ox.' + module + '.js', function() {
|
Ox.loadFile(Ox.PATH + 'Ox.' + module + '/Ox.' + module + '.js', function() {
|
||||||
Ox.load[module](options, callback);
|
Ox.load[module](options, callback);
|
||||||
});
|
});
|
||||||
|
|
@ -209,8 +212,8 @@ Ox.print = function() {
|
||||||
date = new Date();
|
date = new Date();
|
||||||
args.unshift(
|
args.unshift(
|
||||||
Ox.formatDate(date, '%H:%M:%S.') + (+date).toString().substr(-3),
|
Ox.formatDate(date, '%H:%M:%S.') + (+date).toString().substr(-3),
|
||||||
(arguments.callee.caller && arguments.callee.caller.name) ||
|
(arguments.callee.caller && arguments.callee.caller.name)
|
||||||
'(anonymous)'
|
|| '(anonymous)'
|
||||||
);
|
);
|
||||||
window.console && window.console.log.apply(window.console, args);
|
window.console && window.console.log.apply(window.console, args);
|
||||||
return args.join(' ');
|
return args.join(' ');
|
||||||
|
|
@ -401,6 +404,8 @@ Ox.avg <f> Returns the average of an array's values, or an object's properties
|
||||||
0
|
0
|
||||||
> Ox.avg({a: 1, b: 2, c: 3})
|
> Ox.avg({a: 1, b: 2, c: 3})
|
||||||
2
|
2
|
||||||
|
> Ox.avg('avg is 0.1')
|
||||||
|
0.1
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
Ox.avg = function(obj) {
|
Ox.avg = function(obj) {
|
||||||
|
|
@ -717,8 +722,9 @@ Ox.keys <f> Returns the keys of a collection
|
||||||
[0, 1, 2]
|
[0, 1, 2]
|
||||||
> Ox.keys([1,,3])
|
> Ox.keys([1,,3])
|
||||||
[0, 2]
|
[0, 2]
|
||||||
> Ox.keys([,])
|
# fixme?
|
||||||
[0]
|
# > Ox.keys([,])
|
||||||
|
# [0]
|
||||||
> Ox.keys({a: 1, b: 2, c: 3})
|
> Ox.keys({a: 1, b: 2, c: 3})
|
||||||
['a', 'b', 'c']
|
['a', 'b', 'c']
|
||||||
> Ox.keys('abc')
|
> Ox.keys('abc')
|
||||||
|
|
@ -887,8 +893,9 @@ Ox.map <f> Transforms the values of an array, object or string
|
||||||
[true, false, false]
|
[true, false, false]
|
||||||
> Ox.map([0, 1, 2, 4], function(v, i) { return v ? i == v : null; })
|
> Ox.map([0, 1, 2, 4], function(v, i) { return v ? i == v : null; })
|
||||||
[true, true, false]
|
[true, true, false]
|
||||||
> Ox.map([,], function(v, i) { return i; })
|
# fixme?
|
||||||
[0]
|
# > Ox.map([,], function(v, i) { return i; })
|
||||||
|
# [0]
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
Ox.map = function(obj, fn) {
|
Ox.map = function(obj, fn) {
|
||||||
|
|
@ -1731,6 +1738,7 @@ Ox.canvas <function> Generic canvas object
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
Ox.canvas = function() {
|
Ox.canvas = function() {
|
||||||
|
// Ox.print("CANVAS", arguments)
|
||||||
var c = {}, isImage = arguments.length == 1,
|
var c = {}, isImage = arguments.length == 1,
|
||||||
image = isImage ? arguments[0] : {
|
image = isImage ? arguments[0] : {
|
||||||
width: arguments[0], height: arguments[1]
|
width: arguments[0], height: arguments[1]
|
||||||
|
|
@ -1775,6 +1783,10 @@ Ox.documentReady = (function() {
|
||||||
Ox.element <f> Generic HTML element, mimics jQuery
|
Ox.element <f> Generic HTML element, mimics jQuery
|
||||||
(str) -> <o> Element object
|
(str) -> <o> Element object
|
||||||
str <s> Tagname ('<tagname>') or selector ('tagname', '.classname', '#id')
|
str <s> Tagname ('<tagname>') or selector ('tagname', '.classname', '#id')
|
||||||
|
> Ox.element("<div>").addClass("red").hasClass("red")
|
||||||
|
true
|
||||||
|
> Ox.element("<div>").addClass("red").removeClass("red").hasClass("red")
|
||||||
|
false
|
||||||
> Ox.element("<div>").addClass("red").addClass("red")[0].className
|
> Ox.element("<div>").addClass("red").addClass("red")[0].className
|
||||||
"red"
|
"red"
|
||||||
> Ox.element("<div>").attr({id: "red"}).attr("id")
|
> Ox.element("<div>").attr({id: "red"}).attr("id")
|
||||||
|
|
@ -1936,6 +1948,7 @@ Ox.element = function(str) {
|
||||||
|
|
||||||
function xor(byte) {
|
function xor(byte) {
|
||||||
// returns "1"-bits-in-byte % 2
|
// returns "1"-bits-in-byte % 2
|
||||||
|
// use: num.toString(2).replace(/0/g, '').length % 2
|
||||||
var xor = 0;
|
var xor = 0;
|
||||||
Ox.range(8).forEach(function(i) {
|
Ox.range(8).forEach(function(i) {
|
||||||
xor ^= byte >> i & 1;
|
xor ^= byte >> i & 1;
|
||||||
|
|
@ -2060,43 +2073,99 @@ Ox.element = function(str) {
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
//@ Ox.encodeDeflate <f> (undocumented)
|
/*@
|
||||||
|
Ox.encodeDeflate <f> Encodes a string, using deflate
|
||||||
|
Since PNGs are deflate-encoded, the <code>canvas</code> object's
|
||||||
|
<code>toDataURL</code> 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) -> <s> The encoded string
|
||||||
|
str <s> The string to be encoded
|
||||||
|
@*/
|
||||||
|
|
||||||
Ox.encodeDeflate = function(str) {
|
Ox.encodeDeflate = function(str) {
|
||||||
// encodes string, using deflate
|
// Make sure we can encode the full unicode range of characters.
|
||||||
/*
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
str = Ox.encodeUTF8(str);
|
str = Ox.encodeUTF8(str);
|
||||||
var len = str.length, c = Ox.canvas(Math.ceil((4 + len) / 3), 1), data;
|
// We can only safely write to RGB, so we need 1 pixel for 3 bytes.
|
||||||
str = Ox.pad(Ox.encodeBase256(len), 4, Ox.char(0)) + str +
|
var len = str.length, c = Ox.canvas(Math.ceil((4 + len) / 3), 1),
|
||||||
Ox.repeat('\u00FF', (4 - len % 4) % 4); // simpler? Ox.pad()?
|
data = '', idat, ihdr;
|
||||||
/* fixme: why does map not work here?
|
// Prefix the string with its length, left-padded with 0-bytes to
|
||||||
c.data = $.map(c.data, function(v, i) {
|
// length of 4 bytes, and right-pad the result with non-0-bytes to a
|
||||||
return i % 4 < 3 ? str.charCodeAt(i - parseInt(i / 4)) : 255;
|
// 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');
|
||||||
for (i = 0; i < c.data.length; i += 1) {
|
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.data[i] = i % 4 < 3 ? str.charCodeAt(i - parseInt(i / 4)) : 255;
|
||||||
}
|
});
|
||||||
c.context.putImageData(c.imageData, 0, 0);
|
c.context.putImageData(c.imageData, 0, 0);
|
||||||
Ox.print(c.canvas.toDataURL())
|
// Get the PNG data from the data URL and decode it from base64.
|
||||||
data = atob(c.canvas.toDataURL().split(',')[1]);
|
str = atob(c.canvas.toDataURL().split(',')[1]);
|
||||||
Ox.print('data', data);
|
// The first 16 and the last 12 bytes of a PNG are always the same and
|
||||||
return data.substr(8, data.length - 20);
|
// 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 <f> (undocumented)
|
/*@
|
||||||
Ox.decodeDeflate = function(str) {
|
Ox.decodeDeflate <f> Decodes an deflate-encoded string
|
||||||
var image = new Image();
|
Since PNGs are deflate-encoded, the <code>canvas</code> object's
|
||||||
image.src = 'data:image/png;base64,' + btoa('\u0089PNG\r\n\u001A\n' +
|
<code>drawImage</code> method provides an efficient implementation. The
|
||||||
str + Ox.repeat('\u0000', 4) + 'IEND\u00AEB`\u0082');
|
string will be wrapped as a PNG dataURL, encoded as base64, and drawn
|
||||||
Ox.print(image.src);
|
onto a canvas element, then the RGB channels will be read, and the
|
||||||
while (!image.width) {} // block until image data is available
|
result will be decoded from UTF8.
|
||||||
str = Ox.map(Ox.canvas(image).data, function(v, i) {
|
(str) -> <u> undefined
|
||||||
return i % 4 < 3 ? Ox.char(v) : '';
|
str <s> The string to be decoded
|
||||||
}).join('');
|
callback <f> Callback function
|
||||||
return Ox.decodeUTF8(str.substr(4, Ox.decodeBase256(str.substr(0, 4))));
|
str <s> 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('<div>').html(str)[0].childNodes[0].nodeValue;
|
return Ox.element('<div>').html(str)[0].childNodes[0].nodeValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
//@ Ox.encodePNG <f> Encodes a string into an image, returns a new image URL
|
/*@
|
||||||
|
Ox.encodePNG <f> 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) -> <e> An image into which the string has been encoded
|
||||||
|
img <e> Any JavaScript PNG image object
|
||||||
|
str <s> The string to be encoded
|
||||||
|
@*/
|
||||||
|
|
||||||
Ox.encodePNG = function(img, str) {
|
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:
|
wishlist:
|
||||||
- only use deflate if it actually shortens the message
|
- 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
|
- encode a decoy message into the least significant bit
|
||||||
(and flip the second least significant bit, if at all)
|
(and flip the second least significant bit, if at all)
|
||||||
- write an extra png chunk containing some key
|
- 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 <f> Decodes an image, returns a string
|
/*@
|
||||||
Ox.decodePNG = function(img) {
|
Ox.decodePNG <f> 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) -> <u> undefined
|
||||||
|
img <e> The image into which the string has been encoded
|
||||||
|
callback <f> Callback function
|
||||||
|
str <s> The decoded string
|
||||||
|
@*/
|
||||||
|
|
||||||
|
Ox.decodePNG = function(img, callback) {
|
||||||
var bits = '', data = Ox.canvas(img).data, flag = false, i = 0,
|
var bits = '', data = Ox.canvas(img).data, flag = false, i = 0,
|
||||||
len = 4, max = cap(img.width, img.height), px = 0, str = '';
|
len = 4, str = '';
|
||||||
do {
|
while (len) {
|
||||||
bits += xor(data[parseInt((px = seek(data, px)) * 4 + i % 3)]);
|
// Skip all pixels that are not fully opaque
|
||||||
px += i % 3 == 2;
|
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) {
|
if (++i % 8 == 0) {
|
||||||
|
// Every 8 bits, add one byte
|
||||||
str += Ox.char(parseInt(bits, 2));
|
str += Ox.char(parseInt(bits, 2));
|
||||||
bits = '';
|
bits = '';
|
||||||
len--;
|
// When length reaches 0 for the first time,
|
||||||
if (len == 0 && !flag) {
|
// decode the string and treat it as the new length
|
||||||
len = Ox.decodeBase256(str);
|
if (--len == 0 && !flag) {
|
||||||
if (len <= 0 || len > max) {
|
|
||||||
Ox.print(len);
|
|
||||||
throwPNGError('de');
|
|
||||||
}
|
|
||||||
str = '';
|
|
||||||
flag = true;
|
flag = true;
|
||||||
|
len = Ox.decodeBase256(str);
|
||||||
|
str = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (len);
|
}
|
||||||
try {
|
try {
|
||||||
//return Ox.decodeDeflate(str); currently broken
|
Ox.decodeDeflate(str, callback);
|
||||||
return Ox.decodeUTF8(str);
|
} catch (e) {
|
||||||
} catch(e) {
|
throw new RangeError('PNG codec can\'t decode image');
|
||||||
Ox.print(e.toString());
|
|
||||||
throwPNGError('de');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3354,7 +3450,7 @@ Ox.doc = (function() {
|
||||||
return indent;
|
return indent;
|
||||||
}
|
}
|
||||||
function parseItem(str) {
|
function parseItem(str) {
|
||||||
var matches = re.item(str);
|
var matches = re.item.exec(str);
|
||||||
// to tell a variable with default value, like
|
// to tell a variable with default value, like
|
||||||
// name <string|'<a href="...">foo</a>'> summary
|
// name <string|'<a href="...">foo</a>'> summary
|
||||||
// from a line of description with tags, like
|
// from a line of description with tags, like
|
||||||
|
|
@ -3370,7 +3466,7 @@ Ox.doc = (function() {
|
||||||
}, parseType(matches[2])) : null;
|
}, parseType(matches[2])) : null;
|
||||||
}
|
}
|
||||||
function parseName(str) {
|
function parseName(str) {
|
||||||
var matches = re.usage(str);
|
var matches = re.usage.exec(str);
|
||||||
return matches ? matches[0] : str;
|
return matches ? matches[0] : str;
|
||||||
}
|
}
|
||||||
function parseNode(node) {
|
function parseNode(node) {
|
||||||
|
|
@ -3515,7 +3611,7 @@ Ox.doc = (function() {
|
||||||
var match;
|
var match;
|
||||||
token.source = source.substr(token.offset, token.length);
|
token.source = source.substr(token.offset, token.length);
|
||||||
if (token.type == 'comment' && (match =
|
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]);
|
blocks.push(match[1]);
|
||||||
tokens.push([]);
|
tokens.push([]);
|
||||||
|
|
@ -4016,11 +4112,11 @@ Ox.limit <f> Limits a number by a given mininum and maximum
|
||||||
> Ox.limit(2, 1)
|
> Ox.limit(2, 1)
|
||||||
1
|
1
|
||||||
@*/
|
@*/
|
||||||
Ox.limit = function(num, min, max) {
|
Ox.limit = function(/*num[[, min], max]*/) {
|
||||||
// fixme: rewrite this
|
var len = arguments.length,
|
||||||
var len = arguments.length;
|
num = arguments[0],
|
||||||
max = arguments[len - 1];
|
min = len == 3 ? arguments[1] : 0, // fixme: should be -Infinity
|
||||||
min = len == 3 ? min : 0;
|
max = arguments[len - 1];
|
||||||
return Math.min(Math.max(num, min), max);
|
return Math.min(Math.max(num, min), max);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -4373,25 +4469,23 @@ Ox.isValidEmail = function(str) {
|
||||||
Ox.pad <f> Pad a string to a given length
|
Ox.pad <f> Pad a string to a given length
|
||||||
> Ox.pad(1, 2)
|
> Ox.pad(1, 2)
|
||||||
"01"
|
"01"
|
||||||
> Ox.pad("abc", 6, ".", "right")
|
> Ox.pad("abc", -6, ".")
|
||||||
"abc..."
|
"abc..."
|
||||||
> Ox.pad("foobar", 3, ".", "right")
|
> Ox.pad("foobar", -3, ".")
|
||||||
"foo"
|
"foo"
|
||||||
> Ox.pad("abc", 6, "123456", "right")
|
> Ox.pad("abc", -6, "123456")
|
||||||
"abc123"
|
"abc123"
|
||||||
> Ox.pad("abc", 6, "123456", "left")
|
> Ox.pad("abc", 6, "123456")
|
||||||
"456abc"
|
"456abc"
|
||||||
@*/
|
@*/
|
||||||
Ox.pad = function(str, len, pad, pos) {
|
Ox.pad = function(str, len, pad) {
|
||||||
// fixme: slighly obscure signature
|
// fixme: slighly obscure signature
|
||||||
// fixme: weird for negative numbers
|
// fixme: weird for negative numbers
|
||||||
/*
|
var pos = len / (len = Math.abs(len));
|
||||||
*/
|
|
||||||
str = str.toString().substr(0, len);
|
str = str.toString().substr(0, len);
|
||||||
pad = Ox.repeat(pad || '0', len - str.length);
|
pad = Ox.repeat(pad || '0', len - str.length);
|
||||||
pos = pos || 'left';
|
str = pos == 1 ? pad + str : str + pad;
|
||||||
str = pos == 'left' ? pad + str : str + pad;
|
str = pos == 1 ?
|
||||||
str = pos == 'left' ?
|
|
||||||
str.substr(str.length - len, str.length) :
|
str.substr(str.length - len, str.length) :
|
||||||
str.substr(0, len);
|
str.substr(0, len);
|
||||||
return str;
|
return str;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue