various documentation-related changes

This commit is contained in:
rolux 2011-05-05 20:02:56 +02:00
parent b6fdf0c28b
commit a6ed310087
13 changed files with 880 additions and 301 deletions

View file

@ -5,7 +5,7 @@ Ox.load('UI', {
theme: 'modern' theme: 'modern'
}, function() { }, function() {
Ox.loadJSON('json/cities100000.json', function(data) { Ox.getJSON('json/cities100000.json', function(data) {
var listmap = new Ox.ListMap({ var listmap = new Ox.ListMap({
height: window.innerHeight, height: window.innerHeight,
@ -22,7 +22,7 @@ Ox.load('UI', {
size = Math.sqrt(city.population * 100), size = Math.sqrt(city.population * 100),
latSize = size / Ox.EARTH_CIRCUMFERENCE * 360, latSize = size / Ox.EARTH_CIRCUMFERENCE * 360,
lngSize = size * Ox.getDegreesPerMeter(city.latitude); lngSize = size * Ox.getDegreesPerMeter(city.latitude);
return Ox.extend({ return {
countryCode: city.country_code == 'XK' ? 'RS-KO' : city.country_code, countryCode: city.country_code == 'XK' ? 'RS-KO' : city.country_code,
editable: true, editable: true,
flag: city.country_code, flag: city.country_code,
@ -38,7 +38,7 @@ Ox.load('UI', {
west: city.longitude - lngSize / 2, west: city.longitude - lngSize / 2,
north: city.latitude + latSize / 2, north: city.latitude + latSize / 2,
east: city.longitude + lngSize / 2 east: city.longitude + lngSize / 2
}); };
}), }),
width: window.innerWidth width: window.innerWidth
}) })

View file

@ -3,7 +3,7 @@
<head> <head>
<title>OxJS Mouse Events Demo</title> <title>OxJS Mouse Events Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="text/javascript" src="../../build/js/Ox.js"></script> <script type="text/javascript" src="../../build/Ox.js"></script>
<script type="text/javascript" src="js/mouse.js"></script> <script type="text/javascript" src="js/mouse.js"></script>
</head> </head>
<body></body> <body></body>

View file

@ -1,4 +1,6 @@
Ox.load('UI', function() { Ox.load('UI', {
debug: true
}, function() {
var $target = Ox.Element() var $target = Ox.Element()
.css({ .css({
position: 'absolute', position: 'absolute',
@ -36,7 +38,7 @@ Ox.load('UI', function() {
'anyclick', 'singleclick', 'doubleclick', 'mouserepeat', 'anyclick', 'singleclick', 'doubleclick', 'mouserepeat',
'dragstart', 'drag', 'dragpause', 'dragend' 'dragstart', 'drag', 'dragpause', 'dragend'
].forEach(function(event) { ].forEach(function(event) {
$target.bindEvent(event, function(foo, e) { $target.bindEvent(event, function(e) {
var date = new Date(); var date = new Date();
$('<div>') $('<div>')
.html( .html(

View file

@ -2,7 +2,7 @@
OxUI Documentation OxUI Documentation
***/ ***/
var app = {}; var app = {};
$(function() { Ox.load('UI', {debug: true}, function() {
app.$body = $('body'); app.$body = $('body');
app.$document = $(document); app.$document = $(document);
app.$window = $(window); app.$window = $(window);

View file

@ -3,11 +3,8 @@
<head> <head>
<title>oxjs API</title> <title>oxjs API</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="text/javascript" src="../build/jquery/jquery.js"></script>
<link rel="stylesheet" type="text/css" href="../build/css/ox.ui.css"/> <script type="text/javascript" src="../build/Ox.js"></script>
<script type="text/javascript" src="../build/js/jquery.js"></script>
<script type="text/javascript" src="../build/js/OxUI.js"></script>
<script type="text/javascript" src="api.js"></script> <script type="text/javascript" src="api.js"></script>
</head> </head>
<body> <body>

View file

@ -1,4 +1,28 @@
// vim: et:ts=4:sw=4:sts=4:ft=js // vim: et:ts=4:sw=4:sts=4:ft=js
/*@
Ox.Calendar <f> Basic calendar object
() -> <f> Calendar object
(options) -> <f> Calendar object
(options, self) -> <f> Calendar object
options <o> Options object
date <d|new Date()> UTC Date on which the calendar is centered
dates <[o]|[]> Date objects to be displayed
end <s> End of the event (UTC Date, as string)
name <s> Name of the event
start <s> Start of the event (UTC Date, as string)
type <s> Type of the event (like "person")
height <n|256> Height in px
range <[n]|[100, 5101]> Start and end year of the calendar
width <n|256> Width in px
zoom <n|8> Initial zoom level
self <o> Shared private variable
@*/
// Fixme: switch to UTC
// Fixme: in options.dates, replace "stop" with "end"
// Fixme: create a variable-resolution date type (with end that is _inclusive_)
Ox.Calendar = function(options, self) { Ox.Calendar = function(options, self) {
self = self || {}; self = self || {};
@ -6,9 +30,9 @@ Ox.Calendar = function(options, self) {
.defaults({ .defaults({
date: new Date(), date: new Date(),
dates: [], dates: [],
height: 512, height: 256,
range: [100, 5101], range: [1000, 3000],
width: 512, width: 256,
zoom: 8 zoom: 8
}) })
.options(options || {}) .options(options || {})

View file

@ -1,11 +1,67 @@
// vim: et:ts=4:sw=4:sts=4:ft=js // vim: et:ts=4:sw=4:sts=4:ft=js
// check out http://ejohn.org/apps/learn/#36 (-#38, making fns work w/o new) // check out http://ejohn.org/apps/learn/#36 (-#38, making fns work w/o new)
Ox.Element = function() { /*@
Ox.Element <function:Ox.JQueryElement> Basic UI element object
# Usage --------------------------------------------------------------------
([options[, self]]) -> <object> UI element
# Arguments ----------------------------------------------------------------
options <object> the options of the element
# Properties
element <string> tagname or CSS selector
tooltip <object> tooltip (not implemented)
options <string> tagname or CSS selector
self <object> shared private variable
# Properties ---------------------------------------------------------------
$element <object> jQuery DOM object
bindEvent <function> binds an event to a function, once
# Usage
(event, callback) -> <object> this element
({event: callback, ...}) -> <object> this element
# Arguments
event <string> event name
callback <function> callback function
data <object> event data
bindEventOnce <function> binds an event to a function
defaults <function> sets the default options
options <function> sets the options
triggerEvent <function> triggers an event
unbindEvent <function> unbinds an event
# Events -------------------------------------------------------------------
anyclick <event> anyclick
fires on mouseup, but not on any subsequent mouseup within 250 ms
* <*> original event properties
doubleclick <event> doubleclick
fires on the second mousedown within 250 ms
* <*> original event properties
drag <event> drag
fires on mousemove after dragstart, stops firing on mouseup
clientDX <number> horizontal drag delta in px
clientDY <number> vertical drag delta in px
* <*> original event properties
dragend <event> dragpause
Fires on mouseup after dragstart
clientDX <number> horizontal drag delta in px
clientDY <number> vertical drag delta in px
* <*> original event properties
dragpause <event> dragpause
Fires once when the mouse doesn't move for 250 ms after drag
clientDX <number> horizontal drag delta in px
clientDY <number> vertical drag delta in px
* <*> original event properties
dragstart <event> dragstart
fires when the mouse is down for 250 ms
* <*> original event properties
mouserepeat <event> mouserepeat
fires every 50 ms after the mouse was down for 250 ms, stops firing on
mouseleave or mouseup
* <*> original event properties
singleclick <event> singleclick
fires 250 ms after mouseup, if there was no subsequent mousedown
* <*> original event properties
@*/
/*** Ox.Element = function() {
Basic element object
***/
/* /*
tooltip option can be any of the following: tooltip option can be any of the following:
@ -137,7 +193,7 @@ Ox.Element = function() {
that.triggerEvent('mouserepeat'); that.triggerEvent('mouserepeat');
} }
function mouseup(e) { function mouseup(e) {
// only trigger on firse mouseup // only trigger on first mouseup
if (!self.mouseup) { if (!self.mouseup) {
that.triggerEvent('anyclick', e); that.triggerEvent('anyclick', e);
self.mouseup = true; self.mouseup = true;
@ -173,19 +229,29 @@ Ox.Element = function() {
that._self = self; // fixme: remove that._self = self; // fixme: remove
/*@
bindEvent <function> Binds a function to an event
(event, callback) -> <f> This element
({event: callback, ...}) -> <f> This element
callback <f> Callback function
data <o> event data (key/value pairs)
event <s> Event name (can be namespaced, like "click.foo")
@*/
that.bindEvent = function() { that.bindEvent = function() {
/***
binds a function to an event triggered by this object
Usage
bindEvent(event, fn)
bindEvent({eventA: fnA, eventB: fnB, ...})
***/
Ox.forEach(Ox.makeObject(arguments), function(fn, event) { Ox.forEach(Ox.makeObject(arguments), function(fn, event) {
bind('bind', event, fn); bind('bind', event, fn);
}); });
return that; return that;
} }
/*@
bindEventOnce <function> Binds a function to an event, once
(event, callback) -> <f> This element
({event: callback, ...}) -> <f> This element
callback <f> Callback function
data <o> event data (key/value pairs)
event <s> Event name (can be namespaced, like "click.foo")
@*/
that.bindEventOnce = function() { that.bindEventOnce = function() {
Ox.forEach(Ox.makeObject(arguments), function(fn, event) { Ox.forEach(Ox.makeObject(arguments), function(fn, event) {
bind('one', event, fn); bind('one', event, fn);
@ -193,6 +259,13 @@ Ox.Element = function() {
return that; return that;
}; };
/*@
Sets the default options for an element object
({key: value, ...}) -> <f>
key <str> the name of the default option
that <obj> the element object
value <val> the value of the default option
@*/
that.defaults = function(defaults) { that.defaults = function(defaults) {
// sets the default options // sets the default options
self.defaults = defaults; self.defaults = defaults;
@ -200,43 +273,54 @@ Ox.Element = function() {
return that; return that;
}; };
/*@
Makes an element object gain focus
() -> that
that <obj> the element object
@*/
that.gainFocus = function() { that.gainFocus = function() {
/***
make this object gain focus
***/
Ox.Focus.focus(that.id); Ox.Focus.focus(that.id);
return that; return that;
}; };
/*@
Returns true if an element object has focus
() -> hasFocus
hasFocus <boolean> true if the element has focus
@*/
that.hasFocus = function() { that.hasFocus = function() {
/***
returns true if this object has focus
***/
return Ox.Focus.focused() == that.id; return Ox.Focus.focused() == that.id;
}; };
/*@
Makes an element object lose focus
() -> that
that <object> the element object
@*/
that.loseFocus = function() { that.loseFocus = function() {
/***
make this object lose focus
***/
Ox.Focus.blur(that.id); Ox.Focus.blur(that.id);
return that; return that;
}; };
/*@
.options()
Gets or sets the options of an element object
# Usage
() -> <obj> all options
(key) -> <val> the value of option[key]
(key, value) -> <obj> this element
sets options[key] to value and calls self.setOption(key, value)
if the key/value pair was added or modified
({key: value, ...}) -> <obj> this element
sets multiple options and calls self.setOption(key, value)
for every key/value pair that was added or modified
# Arguments
key <str> the name of the option
value <val> the value of the option
@*/
that.options = function() { that.options = function() {
/*
that.options()
returns self.options
that.options(key)
returns self.options.key
that.options(key, val)
sets self.options.key to val, calls self.setOption(key, val)
if the key has been added or its val has changed, returns that
that.options({keyA: valA, keyB: valB})
sets self.options.keyA to valA and self.options.keyB to valB,
calls self.setOptions(key, val) for every key/value pair
that has been added or modified, returns that
*/
return Ox.getset(self.options, arguments, self.setOption, that); return Ox.getset(self.options, arguments, self.setOption, that);
}; };

View file

@ -1,13 +1,20 @@
// vim: et:ts=4:sw=4:sts=4:ft=js // vim: et:ts=4:sw=4:sts=4:ft=js
/*** /*@
Basic jQuery element Ox.JQueryElement <function> Wrapper for jQuery
***/ # Usage
($element) -> <object> Wrapped jQuery DOM element
# Arguments
$element <object> jQuery DOM Element
@*/
Ox.JQueryElement = function($element) { Ox.JQueryElement = function($element) {
var that = this; var that = this;
//@ Ox.JQueryElement.id <number> Unique id
that.id = Ox.uid(); that.id = Ox.uid();
//@ Ox.JQueryElement.ox <string> OxJS version
that.ox = Ox.VERSION; that.ox = Ox.VERSION;
//@ Ox.JQueryElement.$element <object> The jQuery DOM element
that.$element = $element.data({ that.$element = $element.data({
oxid: that.id oxid: that.id
}); });
@ -15,6 +22,7 @@ Ox.JQueryElement = function($element) {
return that; return that;
}; };
// add all jQuery functions to the prototype of Ox.JQueryElement
Ox.forEach($('<div>'), function(val, key) { Ox.forEach($('<div>'), function(val, key) {
if (Ox.isFunction(val)) { if (Ox.isFunction(val)) {
Ox.JQueryElement.prototype[key] = function() { Ox.JQueryElement.prototype[key] = function() {
@ -31,7 +39,7 @@ Ox.forEach($('<div>'), function(val, key) {
// if the $element of an ox object was returned // if the $element of an ox object was returned
// then return the ox object instead // then return the ox object instead
// so that we can do oxObj.jqFn().oxFn() // so that we can do oxObj.jqFn().oxFn()
return ret.jquery && Ox.UI.elements[id = ret.data('oxid')] ? return ret && ret.jquery && Ox.UI.elements[id = ret.data('oxid')] ?
Ox.UI.elements[id] : ret; Ox.UI.elements[id] : ret;
}; };
} }

View file

@ -5,46 +5,7 @@
(function() { (function() {
var buffer = '', var buffer = '', resetTimeout, triggerTimeout;
// the dot notation ('0.numpad') makes the keyboard event ('key_0.numpad')
// namespaced, so that binding to 'key_0' will catch 'key_0.numpad' too
keyNames = {
0: 'section', 8: 'backspace', 9: 'tab', 12: 'clear', 13: 'enter',
16: 'shift', 17: 'control', 18: 'alt', 20: 'capslock', 27: 'escape',
32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home',
37: 'left', 38: 'up', 39: 'right', 40: 'down',
45: 'insert', 46: 'delete', 47: 'help',
48: '0', 49: '1', 50: '2', 51: '3', 52: '4',
53: '5', 54: '6', 55: '7', 56: '8', 57: '9',
65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e',
70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j',
75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o',
80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't',
85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', 90: 'z',
// fixme: this is usually 91: window.left, 92: window.right, 93: select
91: 'meta.left', 92: 'meta.right', 93: 'meta.right',
96: '0.numpad', 97: '1.numpad', 98: '2.numpad', 99: '3.numpad',
100: '4.numpad', 101: '5.numpad', 102: '6.numpad', 103: '7.numpad',
104: '8.numpad', 105: '9.numpad', 106: 'asterisk.numpad', 107: 'plus.numpad',
109: 'minus.numpad', 108: 'enter.numpad', 110: 'dot.numpad', 111: 'slash.numpad',
112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4', 116: 'f5',
117: 'f6', 118: 'f7', 119: 'f8', 120: 'f9', 121: 'f10',
122: 'f11', 123: 'f12', 124: 'f13', 125: 'f14', 126: 'f15', 127: 'f16',
144: 'numlock', 145: 'scrolllock',
186: 'semicolon', 187: 'equal', 188: 'comma', 189: 'minus',
190: 'dot', 191: 'slash', 192: 'backtick', 219: 'openbracket',
220: 'backslash', 221: 'closebracket', 222: 'quote', 224: 'meta'
// see dojo, for ex.
},
// meta comes last so that we can differentiate between
// alt_control_shift_meta.left and alt_control_shift_meta.right
modifierNames = {
altKey: 'alt', // Mac: option
ctrlKey: 'control',
shiftKey: 'shift',
metaKey: 'meta', // Mac: command
},
resetTimeout, triggerTimeout;
/* /*
Ox.UI.ready(function() { Ox.UI.ready(function() {
@ -59,10 +20,10 @@
if ($element.length) { if ($element.length) {
if ( if (
( (
keyNames[event.keyCode] == 'up' && Ox.KEYS[event.keyCode] == 'up' &&
$element[0].selectionStart + $element[0].selectionEnd == 0 $element[0].selectionStart + $element[0].selectionEnd == 0
) || ( ) || (
keyNames[event.keyCode] == 'down' && Ox.KEYS[event.keyCode] == 'down' &&
$element[0].selectionStart == $element.val().length && $element[0].selectionStart == $element.val().length &&
$element[0].selectionEnd == $element.val().length $element[0].selectionEnd == $element.val().length
) )
@ -82,27 +43,41 @@
}); });
function keydown(event) { function keydown(event) {
var focused = Ox.Focus.focused(), var focused = Ox.Focus.focused(),
key, key,
keyName = keyNames[event.keyCode] || '', keyName = Ox.KEYS[event.keyCode] || '',
keyNames = keyName ? [keyName] : [],
keyBasename = keyName.split('.')[0], keyBasename = keyName.split('.')[0],
keys = keyName ? [keyName] : []; ret = true;
Ox.forEach(modifierNames, function(v, k) {
Ox.forEach(Ox.MODIFIER_KEYS, function(v, k) {
// avoid pushing modifier twice // avoid pushing modifier twice
if (event[k] && keyBasename != v) { if (event[k] && keyBasename != v) {
keys.splice(-1, 0, v); keyNames.splice(-1, 0, v);
} }
}); });
key = keys.join('_'); key = keyNames.join('_');
if (/^(shift_)?[a-z]$|^\d(\.numpad)?$|space/.test(key)) { if (focused !== null) {
Ox.UI.elements[focused].triggerEvent('key_' + key);
// prevent Chrome from going back in history, or scrolling
if (
[
'backspace', 'down', 'left', 'right', 'space', 'up'
].indexOf(keyBasename) > -1 &&
!Ox.UI.elements[focused].hasClass('OxInput')
) {
ret = false;
}
}
if (/^[\w\d](\.numpad)?$|space/.test(key)) {
// don't register leading spaces or trailing double spaces // don't register leading spaces or trailing double spaces
if (!(keyName == 'space' && (buffer == '' || / $/.test(buffer)))) { if (!(keyName == 'space' && (buffer == '' || / $/.test(buffer)))) {
buffer += keyName == 'space' ? ' ' : keyBasename; buffer += keyName == 'space' ? ' ' : keyBasename;
Ox.print('buffer', buffer)
// clear the trigger timeout only if the key went into the buffer // clear the trigger timeout only if the key went into the buffer
clearTimeout(triggerTimeout); clearTimeout(triggerTimeout);
triggerTimeout = setTimeout(function() { triggerTimeout = setTimeout(function() {
Ox.print('buffer', buffer)
focused !== null && Ox.UI.elements[focused].triggerEvent('keys', { focused !== null && Ox.UI.elements[focused].triggerEvent('keys', {
keys: buffer keys: buffer
}); });
@ -114,16 +89,10 @@
resetTimeout = setTimeout(function() { resetTimeout = setTimeout(function() {
buffer = ''; buffer = '';
}, 1000); }, 1000);
if (focused !== null) {
Ox.UI.elements[focused].triggerEvent('key_' + key); Ox.print(ret)
// prevent Chrome from going back in history, or scrolling return ret;
if (
['backspace', 'down', 'left', 'right', 'space', 'up'].indexOf(key) > -1 &&
!Ox.UI.elements[focused].hasClass('OxInput')
) {
return false;
}
}
} }
})(); })();

View file

@ -39,12 +39,11 @@ Ox.SyntaxHighlighter = function(options, self) {
self.source = ''; self.source = '';
self.tokens = Ox.tokenize(self.options.source); self.tokens = Ox.tokenize(self.options.source);
self.tokens.forEach(function(token, i) { self.tokens.forEach(function(token, i) {
var classNames, tokenString; var classNames;
if ( if (
!(self.options.stripComments && token.type == 'comment') !(self.options.stripComments && token.type == 'comment')
) { ) {
classNames = 'Ox' + Ox.toTitleCase(token.type); classNames = 'Ox' + Ox.toTitleCase(token.type);
tokenString = self.options.source.substr(self.cursor, token.length);
if (token.type == 'whitespace') { if (token.type == 'whitespace') {
if (isAfterLinebreak() && hasIrregularSpaces()) { if (isAfterLinebreak() && hasIrregularSpaces()) {
classNames += ' OxLeading' classNames += ' OxLeading'
@ -53,7 +52,7 @@ Ox.SyntaxHighlighter = function(options, self) {
} }
} }
self.source += '<span class="' + classNames + '">' + self.source += '<span class="' + classNames + '">' +
encodeToken(tokenString, token.type) + '</span>'; encodeToken(token.source, token.type) + '</span>';
} }
self.cursor += token.length; self.cursor += token.length;
function isAfterLinebreak() { function isAfterLinebreak() {
@ -65,7 +64,7 @@ Ox.SyntaxHighlighter = function(options, self) {
self.tokens[i + 1].type == 'linebreak'; self.tokens[i + 1].type == 'linebreak';
} }
function hasIrregularSpaces() { function hasIrregularSpaces() {
return tokenString.split('').reduce(function(prev, curr) { return token.source.split('').reduce(function(prev, curr) {
return prev + (curr == ' ' ? 1 : 0); return prev + (curr == ' ' ? 1 : 0);
}, 0) % self.options.tabLength; }, 0) % self.options.tabLength;
} }
@ -108,24 +107,24 @@ Ox.SyntaxHighlighter = function(options, self) {
.html(self.source) .html(self.source)
.appendTo(that); .appendTo(that);
function encodeToken(str, type) { function encodeToken(source, type) {
var linebreak = '<br/>', var linebreak = '<br/>',
tab = Ox.repeat('&nbsp;', self.options.tabLength); tab = Ox.repeat('&nbsp;', self.options.tabLength);
if (self.options.showLinebreaks) { if (self.options.showLinebreaks) {
if (type == 'linebreak') { if (type == 'linebreak') {
linebreak = '' + linebreak; linebreak = '\u21A9' + linebreak;
} else { } else {
linebreak = '<span class="OxLinebreak"></span>' + linebreak; linebreak = '<span class="OxLinebreak">\u21A9</span>' + linebreak;
} }
} }
if (self.options.showTabs) { if (self.options.showTabs) {
tab = '<span class="OxTab">\u2192' + tab.substr(6) + '</span>'; tab = '<span class="OxTab">\u2192' + tab.substr(6) + '</span>';
} }
str = Ox.encodeHTML(str) source = Ox.encodeHTML(source)
.replace(/ /g, '&nbsp;') .replace(/ /g, '&nbsp;')
.replace(/\t/g, tab) .replace(/\t/g, tab)
.replace(/\n/g, linebreak); .replace(/\n/g, linebreak);
return str; return source;
} }
self.setOption = function() { self.setOption = function() {

View file

@ -1,4 +1,14 @@
// vim: et:ts=4:sw=4:sts=4:ft=js // vim: et:ts=4:sw=4:sts=4:ft=js
/*@
Basic list object
(options) -> that
(options, self) -> that
options <obj> the list's options
self <obj> shared private variable
@*/
Ox.List = function(options, self) { Ox.List = function(options, self) {
/*** /***
@ -17,23 +27,26 @@ Ox.List = function(options, self) {
var self = self || {}, var self = self || {},
that = new Ox.Container({}, self) that = new Ox.Container({}, self)
.defaults({ .defaults({
centered: false, centered: false, //@ <boo> if true, and orientation is 'horizontal',
construct: null, //@ then keep the selected item centered
draggable: false, construct: null, //@ <fun> (data) returns the list item HTML
format: [], draggable: false, //@ <boo> true if the items can be reordered
itemHeight: 16, format: [], //@ <arr> ???
items: null, itemHeight: 16, //@ <num> item height
itemWidth: 16, items: null, //@ <arr> list items
keys: [], //@ <fun> (data) returns {items, size, ...}
max: -1, //@ (data, callback) returns [items]
min: 0, itemWidth: 16, //@ <num> item width
orientation: 'vertical', keys: [], //@ <arr> keys of the list items
pageLength: 100, max: -1, //@ <num> max number of items that can be selected
selected: [], min: 0, //@ <num> min number of items that must be selected
sort: [], orientation: 'vertical', //@ <str> 'horizontal' or 'vertical'
sortable: false, pageLength: 100, //@ <num> number of items per page
type: 'text', selected: [], //@ <arr> ids of the selected elements
unique: '' sort: [], //@ <arr>
sortable: false, //@ <boo>
type: 'text', //@ <str>
unique: '' //@ <str> name of the key that acts as unique id
}) })
.options(options || {}) .options(options || {})
.scroll(scroll); .scroll(scroll);

View file

@ -1,5 +1,89 @@
// vim: et:ts=4:sw=4:sts=4:ft=js // vim: et:ts=4:sw=4:sts=4:ft=js
/*@
Ox.Map <function> Basic map object
# DESCRIPTION --------------------------------------------------------------
<code>Ox.Map</code> is a wrapper around the
<a href="http://code.google.com/apis/maps/documentation/javascript/">Google
Maps API</a>.
# USAGE --------------------------------------------------------------------
() -> <f> Map object
(options) -> <f> Map object
(options, self) -> <f> Map object
# ARGUMENTS ----------------------------------------------------------------
options <o|{}> options
clickable <b|false> If true, clicking on the map finds a place
editable <b|false> If true, places are editable
findPlaceholder <s|"Find"> Placeholder text for the find input element
labels <b|false> If true, show labels on the map
markers <n|100> Maximum number of markers to be displayed
places <[o]|[]> Array of place objects
countryCode <s> ISO 3166 country code
east <n> Longitude of the eastern boundary in degrees
editable <b|false> If true, the place is editable
geoname <s> Geoname (like "Paris, Île-de-France, France")
lat <n> Latitude in degrees
lng <n> Longitude in degrees
markerColor <s|"red"> CSS color of the place marker
markerSize <n|16> size of the place marker in px
name <s> Name (like "Paris")
north <n> Latitude of the northern boundary in degrees
south <n> Latitude of the southern boundary in degrees
type <s> Type (like "city" or "country")
west <n> Longitude of the western boundary in degrees
selected <s|""> Id of the selected place
statusbar <b|false> If true, the map has a statusbar
toolbar <b|false> If true, the map has a toolbar
self <o|{}> Shared private variable
# EVENTS -------------------------------------------------------------------
addplace <!> Fires when a place has been added
place <o> Place object
editplace <!> Fires when a place has been edited
place <o> Place object
geocode <!> Fires when a google geocode request returns
latLng <o|u> Query coordinates, or undefined
lat <n> latitude
lng <n> longitude
address <s|u> Query string, or undefined
results <[o]> Google Maps geocode results
address_components <[o]> Address components
long_name <s> Long name
short_name <s> Short name
types <[s]> Types (like "country" or "political")
formatted_address <s> Formatted address
geometry <o> Geometry
bounds <o> Bounds
northEast <o> North-east corner
lat <n> Latitude
lng <n> Longitude
southWest <o> South-west corner
lat <n> Latitude
lng <n> Longitude
location <o> Location
lat <n> Latitude
lng <n> Longitude
location_type <s> Location type (like "APPROXIMATE")
viewport <o> Viewport
northEast <o> North-east corner
lat <n> Latitude
lng <n> Longitude
southWest <o> South-west corner
lat <n> Latitude
lng <n> Longitude
types <[s]> Types (like "country" or "political")
# EXAMPLES -----------------------------------------------------------------
<script>
// simple
Ox.load('UI', function() {
Ox.Map().appendTo(Ox.UI.$body);
});
</script>
> Ox.Map() === true
false
> Ox.Map() === false
false
@*/
Ox.Map = function(options, self) { Ox.Map = function(options, self) {
self = self || {}; self = self || {};

View file

@ -1,6 +1,16 @@
// vim: et:ts=4:sw=4:sts=4:ft=js // vim: et:ts=4:sw=4:sts=4:ft=js
// 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
/*@
Ox <f> The Ox object
See Ox.wrap for details.
(value) -> <o> wrapped value
value <*> any value
@*/
Ox = function(val) { Ox = function(val) {
return Ox.wrap(val); return Ox.wrap(val);
}; };
@ -11,48 +21,77 @@ Constants
================================================================================ ================================================================================
*/ */
//@ Ox.AMPM <[str]> ['AM', 'PM']
Ox.AMPM = ['AM', 'PM']; Ox.AMPM = ['AM', 'PM'];
//Ox.DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; //@ Ox.DURATIONS <[str]> ['year', 'month', 'day', 'minute', 'second']
Ox.DURATIONS = ['year', 'month', 'day', 'minute', 'second']; Ox.DURATIONS = ['year', 'month', 'day', 'minute', 'second'];
//@ Ox.EARTH_RADIUS <number> Radius of the earth in meters
Ox.EARTH_RADIUS = 6378137; Ox.EARTH_RADIUS = 6378137;
//@ Ox.EARTH_CIRCUMFERENCE <num> Circumference of the earth in meters
Ox.EARTH_CIRCUMFERENCE = Ox.EARTH_RADIUS * 2 * Math.PI; Ox.EARTH_CIRCUMFERENCE = Ox.EARTH_RADIUS * 2 * Math.PI;
//@ Ox.HTML_ENTITIES <object> HTML entities for ... (FIXME)
Ox.HTML_ENTITIES = { Ox.HTML_ENTITIES = {
'"': '&quot;', '&': '&amp;', "'": '&apos;', '<': '&lt;', '>': '&gt;' '"': '&quot;', '&': '&amp;', "'": '&apos;', '<': '&lt;', '>': '&gt;'
}; };
//@ Ox.KEYS <object> Names for key codes
// the dot notation ('0.numpad') allows for namespaced events ('key_0.numpad'),
// so that binding to 'key_0' will catch 'key0.numpad' too
Ox.KEYS = { Ox.KEYS = {
SECTION: 0, BACKSPACE: 8, TAB: 9, CLEAR: 12, ENTER: 13, 0: 'section', 8: 'backspace', 9: 'tab', 12: 'clear', 13: 'enter',
SHIFT: 16, CONTROL: 17, OPTION: 18, PAUSE: 19, CAPSLOCK: 20, 16: 'shift', 17: 'control', 18: 'alt', 20: 'capslock', 27: 'escape',
ESCAPE: 27, SPACE: 32, PAGEUP: 33, PAGEDOWN: 34, END: 35, HOME: 36, 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home',
LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, INSERT: 45, DELETE: 46, HELP: 47, 37: 'left', 38: 'up', 39: 'right', 40: 'down',
0: 48, 1: 49, 2: 50, 3: 51, 4: 52, 5: 53, 6: 54, 7: 55, 8: 56, 9: 57, 45: 'insert', 46: 'delete', 47: 'help',
A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, 48: '0', 49: '1', 50: '2', 51: '3', 52: '4',
K: 75, L: 76, M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, 53: '5', 54: '6', 55: '7', 56: '8', 57: '9',
U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90, 65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e',
META_LEFT: 91, META_RIGHT: 92, SELECT: 93, 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j',
'0_NUMPAD': 96, '1_NUMPAD': 97, '2_NUMPAD': 98, '3_NUMPAD': 99, 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o',
'4_NUMPAD': 100, '5_NUMPAD': 101, '6_NUMPAD': 102, '7_NUMPAD': 103, 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't',
'8_NUMPAD': 104, '9_NUMPAD': 105, '*_NUMPAD': 106, '+_NUMPAD': 107, 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', 90: 'z',
'\n_NUMPAD': 108, '-_NUMPAD': 109, '._NUMPAD': 110, '/_NUMPAD': 111, // fixme: this is usually 91: window.left, 92: window.right, 93: select
F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117, F7: 118, 91: 'meta.left', 92: 'meta.right', 93: 'meta.right',
F8: 110, F9: 120, F10: 121, F11: 122, F12: 123, F13: 124, F14: 125, 96: '0.numpad', 97: '1.numpad', 98: '2.numpad', 99: '3.numpad',
F15: 126, F16: 127, NUMLOCK: 144, SCROLLLOCK: 145, 100: '4.numpad', 101: '5.numpad', 102: '6.numpad', 103: '7.numpad',
';': 186, '=': 187, ',': 188, '-': 189, '.': 190, '/': 191, '`': 192, 104: '8.numpad', 105: '9.numpad', 106: 'asterisk.numpad', 107: 'plus.numpad',
'(': 219, '\\': 220, ')': 221, '\'': 222 109: 'minus.numpad', 108: 'enter.numpad', 110: 'dot.numpad', 111: 'slash.numpad',
}; 112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4', 116: 'f5',
Ox.MAP_TILE_SIZE = 256; 117: 'f6', 118: 'f7', 119: 'f8', 120: 'f9', 121: 'f10',
122: 'f11', 123: 'f12', 124: 'f13', 125: 'f14', 126: 'f15', 127: 'f16',
144: 'numlock', 145: 'scrolllock',
186: 'semicolon', 187: 'equal', 188: 'comma', 189: 'minus',
190: 'dot', 191: 'slash', 192: 'backtick', 219: 'openbracket',
220: 'backslash', 221: 'closebracket', 222: 'quote', 224: 'meta'
// see dojo, for ex.
},
Ox.MAP_TILE_SIZE = 256; // fixme: definitely not needed here
//@ Ox.MODIFIER_KEYS <obj> Names for modifier keys
// meta comes last so that one can differentiate between
// alt_control_shift_meta.left and alt_control_shift_meta.right
Ox.MODIFIER_KEYS = {
altKey: 'alt', // Mac: option
ctrlKey: 'control',
shiftKey: 'shift',
metaKey: 'meta', // Mac: command
}
//@ Ox.MONTHS <[str]> Names of months
Ox.MONTHS = [ Ox.MONTHS = [
'January', 'February', 'March', 'April', 'May', 'June', 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December' 'July', 'August', 'September', 'October', 'November', 'December'
]; ];
//@ Ox.SHORT_MONTHS <[str]> Short names of months
Ox.SHORT_MONTHS = Ox.MONTHS.map(function(val) { Ox.SHORT_MONTHS = Ox.MONTHS.map(function(val) {
return val.substr(0, 3); return val.substr(0, 3);
}); });
//@ Ox.PATH <str> Path of Ox.js
Ox.PATH = Array.prototype.slice.apply( Ox.PATH = Array.prototype.slice.apply(
document.getElementsByTagName('script') document.getElementsByTagName('script')
).filter(function(element) { ).filter(function(element) {
return /Ox\.js$/.test(element.src); return /Ox\.js$/.test(element.src);
})[0].src.replace('Ox.js', ''); })[0].src.replace('Ox.js', '');
//@ Ox.PREFIXES <arr> ['K', 'M', 'G', 'T', 'P']
Ox.PREFIXES = ['K', 'M', 'G', 'T', 'P']; Ox.PREFIXES = ['K', 'M', 'G', 'T', 'P'];
//@ Ox.SYMBOLS <obj> Unicode characters for symbols
Ox.SYMBOLS = { Ox.SYMBOLS = {
DOLLAR: '\u0024', DOLLAR: '\u0024',
CENT: '\u00A2', POUND: '\u00A3', CURRENCY: '\u00A4', YEN: '\u00A5', CENT: '\u00A2', POUND: '\u00A3', CURRENCY: '\u00A4', YEN: '\u00A5',
@ -78,15 +117,18 @@ Ox.SYMBOLS = {
CLOSE: '\u2715', BALLOT: '\u2717', WINDOWS: '\u2756', CLOSE: '\u2715', BALLOT: '\u2717', WINDOWS: '\u2756',
EDIT: '\uF802', CLICK: '\uF803', APPLE: '\uF8FF' EDIT: '\uF802', CLICK: '\uF803', APPLE: '\uF8FF'
}; };
// local timezone offset in milliseconds //@ Ox.TYPES <[str]> list of types, as returned by Ox.type()
Ox.TYPES = [ Ox.TYPES = [
'Arguments', 'Array', 'Boolean', 'Date', 'Element', 'Function', 'Infinity', 'Arguments', 'Array', 'Boolean', 'Date', 'Element', 'Function', 'Infinity',
'NaN', 'Null', 'Number', 'Object', 'RegExp', 'String', 'Undefined' 'NaN', 'Null', 'Number', 'Object', 'RegExp', 'String', 'Undefined'
]; ];
//@ Ox.VERSION <str> OxJS version number
Ox.VERSION = '0.1.2'; Ox.VERSION = '0.1.2';
//@ Ox.WEEKDAYS <[str]> Names of weekdays
Ox.WEEKDAYS = [ Ox.WEEKDAYS = [
'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
]; ];
//@ Ox.SHORT_WEEKDAYS <[str]> Short names of weekdays
Ox.SHORT_WEEKDAYS = Ox.WEEKDAYS.map(function(val) { Ox.SHORT_WEEKDAYS = Ox.WEEKDAYS.map(function(val) {
return val.substr(0, 3); return val.substr(0, 3);
}); });
@ -97,36 +139,268 @@ Core functions
================================================================================ ================================================================================
*/ */
Ox.getset = function(obj, args, callback, context) { /*@
/*** Ox.doc <f> Generates documentation for annotated JavaScript
Generic getter and setter function (source) <a> Array of documentation objects
source <s> JavaScript source code
> Ox.doc("//@ Ox.foo <s> bar")
[{"name": "Ox.foo", "summary": "bar", "type": "string"}]
@*/
can be implemented like this: Ox.doc = (function() {
var re = {
that.options = function() { item: /^(.+) <(.+)> (.+)$/,
return Ox.getset(options, arguments, setOption(key, val), that); multiline: /^\/\*\@.*?\n([\w\W]+)\n.*?\@\*\/$/,
script: /\n(\s*<script>s*\n[\w\W]+\n\s*<\/script>s*)/g,
singleline: /^\/\/@\s*(.*?)\s*$/,
test: /\n(\s*> .+\n.+?)/g,
usage: /\(.*?\)/
},
types = {
b: 'boolean', d: 'date', e: 'element',
f: 'function', n: 'number', o: 'object',
r: 'regexp', s: 'string', u: 'undefined',
'*': 'any', '!': 'event'
} }
return function(source) {
var blocks = Ox.map(Ox.tokenize(source), function(token) {
// filter out tokens that are not comments
// or don't match the doc comment pattern
var match;
return token.type == 'comment' && (match =
re.multiline(token.source) || re.singleline(token.source)
) ? match[1] : null;
}),
items = [];
blocks.forEach(function(block) {
var item, lastItem,
lines = block
.replace(re.script, encodeLinebreaks)
.replace(re.test, encodeLinebreaks)
.split('\n');
// create a tree and parse its root node
item = parseNode(parseTree(lines));
if (/^[A-Z]/.test(item.name)) {
items.push(item);
} else {
lastItem = items[items.length - 1];
lastItem.properties = lastItem.properties || [];
lastItem.properties.push(item);
}
});
function decodeLinebreaks(match, submatch) {
return (submatch || match).replace(/\u21A9/g, '\n');
}
function encodeLinebreaks(match, submatch) {
return '\n' + (submatch || match).replace(/\n/g, '\u21A9');
}
function getIndent(str) {
var indent = -1;
while (str[++indent] == ' ') {}
return indent;
}
function parseItem(str) {
var matches = re.item(str);
// to tell a variable with default value, like
// name <string|'<a href="...">foo</a>'> summary
// from a line of description with tags, like
// some <a href="...">description</a> text
// we need to check if there is either no forward slash
// or if the second last char is a single or double quote
return matches && (
matches[2].indexOf('/') == -1 ||
'\'"'.indexOf(matches[2].substr(-2, 1)) > -1
) ? Ox.extend({
name: parseName(matches[1].trim()),
summary: matches[3].trim()
}, parseType(matches[2])) : null;
}
function parseName(str) {
var matches = re.usage(str);
return matches ? matches[0] : str;
}
function parseNode(node) {
var item = parseItem(node.line), subitem;
Ox.print(node, node.line, 'item', item);
node.nodes && node.nodes.forEach(function(node) {
var key, line = node.line, subitem;
if (!/^#/.test(node.line)) {
if (/^<script>/.test(line)) {
item.examples = [parseScript(line)];
} else if (/^>/.test(line)) {
item.examples = item.examples || [];
item.examples.push(parseTest(line));
} else if ((subitem = parseItem(line))) {
if (/^\(/.test(subitem.name)) {
item.usage = item.usage || [];
item.usage.push(parseNode(node));
} else if (subitem.types[0] == 'event') {
item.events = item.events || [];
item.events.push(parseNode(node));
} else {
key = item.types[0] == 'function' ?
'arguments' : 'properties'
item[key] = item[key] || [];
item[key].push(parseNode(node));
}
} else {
item.description = item.description ?
item.description + ' ' + line : line
}
}
});
return item;
}
function parseScript(str) {
// remove script tags and extra indentation
var lines = decodeLinebreaks(str).split('\n'),
indent = getIndent(lines[1]);
return {
statement: Ox.map(lines, function(line, i) {
return i && i < lines.length - 1 ?
line.substr(indent) : null;
}).join('\n')
};
}
function parseTest(str) {
var lines = decodeLinebreaks(str).split('\n');
return {
statement: lines[0].substr(2),
result: JSON.parse(lines[1].trim())
};
}
function parseTree(lines) {
// parses indented lines into a tree structure, like
// {line: "...", nodes: [{line: "...", nodes: [...]}]}
var branches = [],
indent,
node = {
// chop the root line
line: lines.shift().trim()
};
if (lines.length) {
indent = getIndent(lines[0]);
lines.forEach(function(line) {
if (getIndent(line) == indent) {
// line is a child,
// make it the root line of a new branch
branches.push([line]);
} else {
// line is a descendant of the last child,
// add it to the last branch
branches[branches.length - 1].push(line);
}
});
node.nodes = branches.map(function(lines) {
return parseTree(lines);
});
}
return node;
}
function parseType(str) {
// returns {types: [""]}
// or {types: [""], default: ""}
// or {types: [""], parent: ""}
var isArray,
ret = {types: []},
split,
type;
// only split by ':' if there is no default string value
if ('\'"'.indexOf(str.substr(-2, 1)) == -1) {
split = str.split(':');
str = split[0];
if (split.length == 2) {
ret.parent = split[1];
}
}
str.split('|').forEach(function(str) {
var unwrapped = unwrap(str);
if (unwrapped in types) {
ret.types.push(wrap(types[unwrapped]))
} else if (
(type = Ox.filter(Ox.values(types), function(type) {
return Ox.startsWith(type, unwrapped);
})).length
) {
ret.types.push(wrap(type[0]))
} else {
ret.default = str;
}
});
function unwrap(str) {
return (isArray = /^\[.+\]$/.test(str)) ?
str = str.substr(1, str.length - 2) : str;
}
function wrap(str) {
return isArray ? 'array[' + str + 's]' : str;
}
return ret;
}
return items;
};
}())
Ox.getset(obj, []) returns obj Ox.get = function(url, callback) {
Ox.getset(obj, [key]) returns obj.key var req = new XMLHttpRequest();
Ox.getset(obj, [key, val], callback, context) req.open('GET', url, true);
Ox.getset(obj, [{key: val, ...}], callback, context) sets obj.key to val, req.onreadystatechange = function() {
calls callback(key, val) if (req.readyState == 4) {
for each changed value, if (req.status == 200) {
returns context callback(req.responseText);
(for chaining) } else {
throw new Error('URL ' + url + ', status ' + req.status);
}
}
};
req.send();
};
>>> o = new function() { var o = {}, s = function() {}, t = this; t.o = function() { return Ox['getset'](o, arguments, s, t); }; return t; } Ox.getJSON = function(url, callback) {
true Ox.get(url, function(data) {
>>> Ox.getset({}, []) && o.o('key', 'val').o('key') callback(JSON.parse(data));
'val' });
>>> Ox.getset({}, []) && o.o({key: 'val', foo: 'bar'}).o().foo }
'bar'
>>> Ox.getset({}, []) && typeof o.o({foo: undefined}).o('foo') == 'undefined' /*@
true Ox.getset <f> Generic getter and setter function
>>> delete o See examples for details.
true # Usage --------------------------------------------------------------------
***/ Ox.getset(options, args=[]) -> <o> all options
Ox.getset(options, args=[key]) -> <*> options[key]
Ox.getset(options, args=[key, value], callback, context) -> <f|o> context
sets options[key] to value and calls fn(key, value)
if the key/value pair was added or modified
Ox.getset(options, args=[{key: value}], callback, context) -> <f|o> context
sets multiple options and calls fn(key, value)
for every key/value pair that was added or modified
# Arguments ----------------------------------------------------------------
options <obj> Options object (key/value pairs)
args <[*]> The arguments "array" of the caller function
callback <fun> Callback function
The callback is called for every key/value pair that was added or
modified.
key <s> Key
value <*> Value
context <obj> The parent object of the caller function (for chaining)
# Examples -----------------------------------------------------------------
<script>
var object = new function() {
var options = {},
setOption = function(key, value) {},
that = this;
that.options = function() {
return Ox.getset(options, arguments, setOption, that);
}
return that;
};
</script>
> object.options("key", "val").options("key")
"val"
> object.options({foo: "foo", bar: "bar"}).options()
{"key": "val", "foo": "foo", "bar": "bar"}
@*/
Ox.getset = function(obj, args, callback, context) {
var obj_ = Ox.clone(obj), ret; var obj_ = Ox.clone(obj), ret;
if (args.length == 0) { if (args.length == 0) {
// getset([]) // getset([])
@ -137,7 +411,6 @@ Ox.getset = function(obj, args, callback, context) {
} else { } else {
// getset([key, val]) or getset([{key: val, ...}]) // getset([key, val]) or getset([{key: val, ...}])
args = Ox.makeObject(args); args = Ox.makeObject(args);
// args = Ox.makeObject(Ox.isObject(args[0]) ? args[0] : args);
obj = Ox.extend(obj, args); obj = Ox.extend(obj, args);
Ox.forEach(args, function(val, key) { Ox.forEach(args, function(val, key) {
if (!obj_ || !Ox.isEqual(obj_[key], val)) { if (!obj_ || !Ox.isEqual(obj_[key], val)) {
@ -149,11 +422,20 @@ Ox.getset = function(obj, args, callback, context) {
return ret; return ret;
} }
/*@
Ox.load <f> Loads a module
(module, callback) -> <u> undefined
(module, options, callback) -> <u> undefined
module <s> Module name
options <o> Module options
callback <f> Callback function
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
loads Ox modules // problem: where do multiple options go?
fixme: no way to load multiple modules // [{name: "", options: {}}, {name: "", options: {}}] isn't pretty
***/
callback = arguments[arguments.length - 1]; callback = arguments[arguments.length - 1];
options = arguments.length == 3 ? arguments[1] : {}; options = arguments.length == 3 ? arguments[1] : {};
Ox.loadFile(Ox.PATH + 'Ox.' + module + '/Ox.' + module + '.js', function() { Ox.loadFile(Ox.PATH + 'Ox.' + module + '/Ox.' + module + '.js', function() {
@ -161,16 +443,29 @@ Ox.load = function(module, options, callback) {
}); });
}; };
/*@
Ox.loadFile <f> Loads a file (image, script or stylesheet)
(file="script.js", callback) -> <u> undefined
(file="stylesheet.css", callback) -> <u> undefined
(file="image.png", callback) -> <e> DOM element
file <s> Local path or remote URL
callback <f> Callback function
@*/
Ox.loadFile = (function() { Ox.loadFile = (function() {
/*** // fixme: this doesn't handle errors yet
loads stylesheets, scripts and images
***/
var cache = {}; var cache = {};
return function (file, callback) { return function (file, callback) {
var element, request, var element,
request,
type = file.split('.').pop(); type = file.split('.').pop();
isImage = type != 'css' && type != 'js';
if (!cache[file]) { if (!cache[file]) {
if (type == 'css' || type == 'js') { if (isImage) {
element = new Image();
element.onload = addFileToCache;
element.src = file;
} else {
if (!findFileInHead()) { if (!findFileInHead()) {
element = document.createElement(type == 'css' ? 'link' : 'script'); element = document.createElement(type == 'css' ? 'link' : 'script');
element[type == 'css' ? 'href' : 'src'] = file; element[type == 'css' ? 'href' : 'src'] = file;
@ -185,22 +480,19 @@ Ox.loadFile = (function() {
} else { } else {
addFileToCache(); addFileToCache();
} }
} else {
element = new Image();
element.onload = addFileToCache;
element.src = file;
} }
} else { } else {
callback(); callback();
} }
function addFileToCache() { function addFileToCache() {
//type == 'svg' && Ox.print('addToCache', file) if (isImage) {
if (type == 'css' || type == 'js') { // for an image, save the element itself,
cache['file'] = true; // so that it remains in the browser cache
callback();
} else {
cache['file'] = element; cache['file'] = element;
callback(element); callback(element);
} else {
cache['file'] = true;
callback();
} }
} }
function findFileInHead() { function findFileInHead() {
@ -227,25 +519,17 @@ Ox.loadFile = (function() {
}; };
}()); }());
Ox.loadJSON = function(url, callback) { /*@
var req = new XMLHttpRequest(); Ox.print <f> Prints its arguments to the console
req.open('GET', url, true); (arg, ...) -> <s> String
req.onreadystatechange = function() { The string contains the timestamp, the name of the caller function, and
if (req.readyState == 4) { any arguments, separated by spaces
if (req.status == 200) { arg <*> any value
callback(JSON.parse(req.responseText)); > Ox.print("foo").substr(-3)
} else { "foo"
throw new Error('URL ' + url + ', status ' + req.status); @*/
}
}
};
req.send();
};
Ox.print = function() { Ox.print = function() {
/*
like console.log, but prepends timestamp and name of the caller function
*/
if (window.console) { if (window.console) {
var args = Ox.makeArray(arguments), var args = Ox.makeArray(arguments),
date = new Date(); date = new Date();
@ -255,27 +539,39 @@ Ox.print = function() {
); );
window.console.log.apply(window.console, args); window.console.log.apply(window.console, args);
} }
return args.join(' ');
}; };
Ox.uid = (function() { /*@
/*** Ox.uid <f> Returns a unique id
returns a unique id () -> <n> Unique id
>>> Ox.uid() != Ox.uid() > Ox.uid() != Ox.uid()
true true
***/ @*/
Ox.uid = (function() {
var uid = 0; var uid = 0;
return function() { return function() {
return uid++; return uid++;
}; };
}()); }());
/*@
Ox.wrap <f> Wraps a value so that one can directly call any Ox function on it
Additionally, chain() allows for chaining, and value() returns the
original value. See examples for details.
(value) -> <o> wrapped value
chain <f> wrap the return value to allow chaining
value <f> unwrap the value wrapped by chain()
value <*> any value
> Ox.wrap("foobar").repeat(2)
"foobarfoobar"
> Ox.wrap("foobar").chain().reverse().toTitleCase().value()
"Raboof"
> Ox.wrap("foobar").value()
"foobar"
@*/
Ox.wrap = function(val, chained) { Ox.wrap = function(val, chained) {
/***
>>> Ox.wrap('foobar').reverse()
'raboof'
>>> Ox.wrap('foobar').chain().reverse().reverse().value()
'foobar'
***/
var wrapper = { var wrapper = {
chain: function() { chain: function() {
wrapper.chained = true; wrapper.chained = true;
@ -845,6 +1141,7 @@ Ox.sum = function(obj) {
}; };
Ox.toArray = function(obj) { Ox.toArray = function(obj) {
// fixme: can this be thrown out?
/* /*
>>> Ox.toArray('foo') >>> Ox.toArray('foo')
['foo'] ['foo']
@ -1027,14 +1324,26 @@ Date functions
================================================================================ ================================================================================
*/ */
/*@
Ox.getDateInWeek <f> Get the date that falls on a given weekday in the same week
# Usage
(date, weekday) -> <dat> Date
(date, weekday, utc) -> <dat> UTC Date
# Arguments
date <d> Date
weekday <n|s> 1-7 (Monday-Sunday) or name, full ("Monday") or short ("Sun")
utc <b> if true, all dates are UTC
# Examples
> Ox.formatDate(Ox.getDateInWeek(new Date("January 1 2000"), "Sunday"), "%A, %B %e, %Y")
"Sunday, January 2, 2000"
> Ox.formatDate(Ox.getDateInWeek(new Date("Jan 1 2000"), "Fri"), "%A, %B %e, %Y")
"Friday, December 31, 1999"
> Ox.formatDate(Ox.getDateInWeek(new Date("1/1/2000"), 1), "%A, %B %e, %Y")
"Monday, December 27, 1999"
@*/
Ox.getDateInWeek = function(date, weekday, utc) { Ox.getDateInWeek = function(date, weekday, utc) {
/* /*
>>> Ox.formatDate(Ox.getDateInWeek(new Date("January 1 2000"), "Sunday"), "%A, %B %e, %Y")
"Sunday, January 2, 2000"
>>> Ox.formatDate(Ox.getDateInWeek(new Date("Jan 1 2000"), "Fri"), "%A, %B %e, %Y")
"Friday, December 31, 1999"
>>> Ox.formatDate(Ox.getDateInWeek(new Date("1/1/2000"), 1), "%A, %B %e, %Y")
"Monday, December 27, 1999"
*/ */
date = Ox.makeDate(date); date = Ox.makeDate(date);
Ox.print(date, Ox.getDate(date, utc), Ox.formatDate(date, '%u', utc), date) Ox.print(date, Ox.getDate(date, utc), Ox.formatDate(date, '%u', utc), date)
@ -1241,6 +1550,19 @@ DOM functions
================================================================================ ================================================================================
*/ */
/*@
Ox.canvas <function> Generic canvas object
Returns an object with the properties: <code>canvas</code>,
<code>context</code>, <code>data</code> and <code>imageData</code>.
# Usage --------------------------------------------------------------------
Ox.canvas(width, height) -> <object> canvas
Ox.canvas(image) -> <object> canvas
# Arguments ----------------------------------------------------------------
width <n> Width in px
height <n> Height in px
image <e> Image object
@*/
Ox.canvas = function() { Ox.canvas = function() {
// Ox.canvas(img) or Ox.canvas(width, height) // Ox.canvas(img) or Ox.canvas(width, height)
var c = {}, isImage = arguments.length == 1, var c = {}, isImage = arguments.length == 1,
@ -1257,6 +1579,11 @@ Ox.canvas = function() {
return c; return c;
}; };
/*@
Ox.documentReady <function> Calls a callback function once the DOM is ready
(callback) -> <boolean> If true, the document was ready
callback <function> Callback function
@*/
Ox.documentReady = (function() { Ox.documentReady = (function() {
var callbacks = []; var callbacks = [];
document.onreadystatechange = function() { document.onreadystatechange = function() {
@ -1270,42 +1597,72 @@ Ox.documentReady = (function() {
}; };
return function(callback) { return function(callback) {
if (document.readyState == 'complete') { if (document.readyState == 'complete') {
//Ox.print('document is ready')
callback(); callback();
return true;
} else { } else {
callbacks.push(callback); callbacks.push(callback);
//Ox.print('document is not ready', callbacks) return false;
} }
} }
}()); }());
/*@
Ox.Element <f> Generic HTML element, mimics jQuery
(str) -> <o> Element object
str <s> Tagname ('<tagname>') or selector ('tagname', '.classname', '#id')
> Ox.element("<div>").addClass("red").addClass("red")[0].classname
"red"
> Ox.element("<div>").attr({id: "red"}).attr("id")
"red"
> Ox.element("<div>").css("color", "red").css("color")
"red"
> Ox.element("<div>").html("red").html()
"red"
@*/
Ox.element = function(str) { Ox.element = function(str) {
/*
Generic HTML element, mimics jQuery
>>> Ox.element('div').attr({id: 'foo'}).attr('id')
'foo'
>>> Ox.element('div').css('color', 'red').css('color')
'red'
>>> Ox.element('div').html('foo').html()
'foo'
*/
return { return {
//@ 0 <e> The DOM element itself
0: str[0] == '<' ? document.createElement(str.substr(1, str.length - 2)) : 0: str[0] == '<' ? document.createElement(str.substr(1, str.length - 2)) :
str[0] == '.' ? document.getElementsByClassName(str.substr(1))[0] : str[0] == '.' ? document.getElementsByClassName(str.substr(1))[0] :
str[0] == '#' ? document.getElementById(str.substr(1)) : str[0] == '#' ? document.getElementById(str.substr(1)) :
document.getElementsByTagName(str)[0], document.getElementsByTagName(str)[0],
/*@
addClass <f> Adds a class name
(className) -> <o> This element
className <s> Class name
@*/
addClass: function(str) { addClass: function(str) {
this[0].className += (this[0].className ? ' ' : '') + str; this[0].className = this[0].className ? Ox.unique(
(this[0].className + ' ' + str).split(' ')
) : str;
return this; return this;
}, },
/*@
append() <f> Appends another element to this element
(element) -> <o> This element
element <o> Another element
@*/
append: function(element) { append: function(element) {
this[0].appendChild(element[0]); this[0].appendChild(element[0]);
return this; return this;
}, },
/*@
appendTo <f> appends this element object to another element object
(element) -> <o> This element
element <o> Another element
@*/
appendTo: function(element) { appendTo: function(element) {
element[0].appendChild(this[0]); element[0].appendChild(this[0]);
return this; return this;
}, },
/*@
attr <f> Gets or sets an attribute
(key) -> <s> Value
(key, value) -> <o> This element
({key, value}) -> <o> This element
key <str> Attribute name
value <str> Attribute value
@*/
attr: function() { attr: function() {
var ret, that = this; var ret, that = this;
if (arguments.length == 1 && Ox.isString(arguments[0])) { if (arguments.length == 1 && Ox.isString(arguments[0])) {
@ -1318,6 +1675,14 @@ Ox.element = function(str) {
} }
return ret; return ret;
}, },
/*@
css <f> Gets or sets a CSS attribute
(key) -> <s> Value
(key, value) -> <o> This element
({key, value}) -> <o> This element
key <str> Attribute name
value <str> Attribute value
@*/
css: function() { css: function() {
var ret, that = this; var ret, that = this;
if (arguments.length == 1 && Ox.isString(arguments[0])) { if (arguments.length == 1 && Ox.isString(arguments[0])) {
@ -1330,6 +1695,12 @@ Ox.element = function(str) {
} }
return ret; return ret;
}, },
/*@
html <f> Gets or sets the inner HTML
() -> <s> The inner HTML
(html) -> <o> This element
html <s> The inner HTML
@*/
html: function(str) { html: function(str) {
var ret; var ret;
if (Ox.isUndefined(str)) { if (Ox.isUndefined(str)) {
@ -1340,8 +1711,27 @@ Ox.element = function(str) {
} }
return ret; return ret;
}, },
mousedown: function(fn) { /*@
this[0].onmousedown = fn; mousedown <f> Binds a function to the mousedown event
(callback) -> <o> This element
callback <f> Callback function
event <o> The DOM event
@*/
mousedown: function(callback) {
this[0].onmousedown = callback;
return this;
},
/*@
removeClass <f> Removes a class name
(className) -> <o> This element
className <s> Class name
@*/
removeClass: function(str) {
this[0].className = Ox.filter(
this[0].className.split(' '), function(className) {
return className != str;
}
).join(' ');
return this; return this;
} }
} }
@ -1429,7 +1819,7 @@ Encoding functions
>>> Ox.encodeBase64(32394) >>> Ox.encodeBase64(32394)
'foo' 'foo'
*/ */
return btoa(Ox.encodeBase256(num)).replace(/=/g, ""); return btoa(Ox.encodeBase256(num)).replace(/=/g, '');
} }
Ox.decodeBase64 = function(str) { Ox.decodeBase64 = function(str) {
@ -1555,7 +1945,6 @@ Encoding functions
// relies on dom, but shorter than using this: // relies on dom, but shorter than using this:
// http://www.w3.org/TR/html5/named-character-references.html // http://www.w3.org/TR/html5/named-character-references.html
return Ox.element('<div>').html(str)[0].childNodes[0].nodeValue; return Ox.element('<div>').html(str)[0].childNodes[0].nodeValue;
//return $('<div/>').html(str)[0].childNodes[0].nodeValue;
}; };
Ox.encodePNG = function(img, str) { Ox.encodePNG = function(img, str) {
@ -2334,9 +2723,10 @@ Ox.parseHTML = (function() {
'&lt;script&gt;alert()&lt;/script&gt;' '&lt;script&gt;alert()&lt;/script&gt;'
*/ */
var defaultTags = [ var defaultTags = [
'a', 'b', 'blockquote', 'cite', 'code', 'del', 'a', 'b', 'blockquote', 'cite', 'code',
'em', 'i', 'img', 'ins', 'li', 'ol', 'q', 'rtl', 'del', 'em', 'i', 'img', 'ins',
's', 'strong', 'sub', 'sup', 'ul', '[]' 'li', 'ol', 'q', 'rtl', 's',
'strong', 'sub', 'sup', 'ul', '[]'
], ],
parse = { parse = {
a: { a: {
@ -2687,6 +3077,7 @@ Ox.startsWith = function(str, sub) {
// fixme: // fixme:
// !!(/^sub/(str)) is shorter than // !!(/^sub/(str)) is shorter than
// Ox.startsWith(str, sub) anyway // Ox.startsWith(str, sub) anyway
// new RegExp('^' + sub).test(str) is longer though...
*/ */
return new RegExp('^' + sub).test(str); return new RegExp('^' + sub).test(str);
}; };
@ -2983,7 +3374,7 @@ Ox.tokenize = (function() {
} }
tokenize[type](); tokenize[type]();
tokens.push({ tokens.push({
length: cursor - start, source: source.substr(start, cursor - start),
type: type, type: type,
}); });
} }
@ -3009,7 +3400,6 @@ Ox.tokenize = (function() {
} else { } else {
prevToken = tokens[index]; prevToken = tokens[index];
prevString = source.substr(cursor - prevToken.length - offset, prevToken.length); prevString = source.substr(cursor - prevToken.length - offset, prevToken.length);
Ox.print('forward slash |', prevToken, prevToken.type, '"'+prevString+'"');
isRegExp = ( isRegExp = (
prevToken.type == 'keyword' && prevToken.type == 'keyword' &&
['false', 'null', 'true'].indexOf(prevString) == -1 ['false', 'null', 'true'].indexOf(prevString) == -1
@ -3098,16 +3488,16 @@ Ox.truncate = function(str, len, pad, pos) {
Ox.words = function(str) { Ox.words = function(str) {
/* /*
>>> Ox.words('He\'s referring to the "ill-conceived" AOL/TimeWarner merger--didn\'t you know?') > Ox.words("The key/value pairs are read-only--aren't they?")
['he\'s', 'referring', 'to' , 'the' , 'ill-conceived' , 'aol', 'timewarner' , 'merger' , 'didn\'t', 'you', 'know'] ["the", "key", "value", "pairs", "are", "read-only", "aren't", "they"]
*/ */
var arr = str.toLowerCase().split(/\b/), var arr = str.toLowerCase().split(/\b/),
chr = "-'", chr = "-'",
len = arr.length, len = arr.length,
startsWithWord = !!/\w/(arr[0]); startsWithWord = !!/\w/(arr[0]);
arr.forEach(function(v, i) { arr.forEach(function(v, i) {
// find single occurrences of chars in chr // find single occurrences of "-" or "-"
// that are not at the beginning or end of str // that are not at the beginning or end of the string
// and join the surrounding words with them // and join the surrounding words with them
if ( if (
i > 0 && i < len - 1 && i > 0 && i < len - 1 &&
@ -3320,46 +3710,55 @@ Ox.isString = function(val) {
return typeof val == 'string'; return typeof val == 'string';
}; };
/*@
Ox.isUndefined <f> Tests if a value is undefined
(value) -> <b> If true, the value is undefined
value <*> any value
> Ox.isUndefined()
true
@*/
Ox.isUndefined = function(val) { Ox.isUndefined = function(val) {
// fixme: void 0 is also nice // fixme: void 0 is also nice
/*
>>> Ox.isUndefined()
true
*/
return typeof val == 'undefined'; return typeof val == 'undefined';
}; };
/*@
Ox.typeOf <f> Returns the type of a value
(value) -> <s> type
value <*> Any value
# Examples
> (function() { return Ox.typeOf(arguments); }())
"arguments"
> Ox.typeOf([])
"array"
> Ox.typeOf(false)
"boolean"
> Ox.typeOf(new Date())
"date"
> Ox.typeOf(document.createElement())
"element"
> Ox.typeOf(function() {})
"function"
> Ox.typeOf(Infinity)
"infinity"
> Ox.typeOf(NaN)
"nan"
> Ox.typeOf(null)
"null"
> Ox.typeOf(0)
"number"
> Ox.typeOf({})
"object"
> Ox.typeOf(/ /)
"regexp"
> Ox.typeOf('')
"string"
> Ox.typeOf()
"undefined"
@*/
Ox.typeOf = function(val) { Ox.typeOf = function(val) {
/*
>>> (function() { return Ox.typeOf(arguments); }())
'arguments'
>>> Ox.typeOf([])
'array'
>>> Ox.typeOf(false)
'boolean'
>>> Ox.typeOf(new Date())
'date'
>>> Ox.typeOf(document.createElement())
'element'
>>> Ox.typeOf(function() {})
'function'
>>> Ox.typeOf(Infinity)
'infinity'
>>> Ox.typeOf(NaN)
'nan'
>>> Ox.typeOf(null)
'null'
>>> Ox.typeOf(0)
'number'
>>> Ox.typeOf({})
'object'
>>> Ox.typeOf(/ /)
'regexp'
>>> Ox.typeOf('')
'string'
>>> Ox.typeOf()
'undefined'
*/
var ret; var ret;
Ox.forEach(Ox.TYPES, function(type) { Ox.forEach(Ox.TYPES, function(type) {
if (Ox['is' + type](val)) { if (Ox['is' + type](val)) {