Permalink
Browse files

after -> bindOnce, gesture fixes, doc fixes

  • Loading branch information...
1 parent e4322f9 commit ccc054b4f2ae23d5b423c58a925a00f6913b6ce7 @voloko committed Mar 15, 2011
View
14 docs/core/function.markdown
@@ -175,17 +175,21 @@ Adds a delegate call function to the given `source`. By default, `targetName` is
fun.delegateCall(Wrapper.prorotype, ['getAttribute', 'setAttribute'],
'node');
-### fun.after(callback)
+### fun.defer(callback)
-Executes `callback` after current execution is finished. Currently uses
-`setTimeout`. If called several times with the same `callback`, will execute
-it only once.
+Executes `callback` after current execution is finished. Uses `fun.defer`.
+Will use `postMessage` if available. Fallbacks to `setTimeout` if not.
+
+### fun.deferOnce(callback, [context])
+
+Same as `defer`. If called several times with the same `callback`, will
+execute it only once.
Redraw only once regardless of the number of children added:
X.prototype.appendChild = function(view) {
Base.prototype.appendChild.call(this, view);
- fun.after(fun.bindOnce(this.redraw, redraw));
+ fun.deferOnce(this.redraw, redraw);
}
### fun.throttle(fn, timeout)
View
6 docs/introduction.markdown
@@ -170,18 +170,18 @@ To use modules separately you need the uki tools server/builder. Once installed
can use `require` in your client-side code. For example, instead of requiring
the full core, you can require only the utility functions:
- var utils = require('uki-core/utils');
+ var utils = require('ukijs/src/uki-core/utils');
var keys = utils.keys({ foo: 1, bar: 2});
- var fun = require('uki-core/function'),
+ var fun = require('ukijs/src/uki-core/function'),
Observable = require('uki-core/observable').Observable;
var Person = fun.newClass(Observable, {
name: Observable.newProp('name'),
age: Observable.newProp('age')
});
- var build = require('uki-core/builder').build;
+ var build = require('ukijs/src/uki-core/builder').build;
var button = build({ view: 'Button', label: 'x' });
View
49 perf/bench.js
@@ -0,0 +1,49 @@
+var uki = require('uki-core');
+
+// min time for coarse estimate of total runtime
+var MIN_TIME_ESTIMATE = 100;
+
+// max number of runs when estimating
+var MAX_RUNS_ESTIMATE = 1e5;
+
+var MIN_TIME_BENCH = 2000;
+
+/**
+* Try always run for at least 2sec, then calculate the time of
+* one call.
+*/
+module.exports = uki.newClass({
+ init: function(name, fn) {
+ this.name = name;
+ this._fn = fn;
+ },
+
+ bench: function() {
+ var e = this._estimate(),
+ k = MIN_TIME_BENCH / e.t,
+ n = e.n * k << 0;
+
+ return this._run(n) / n;
+ },
+
+ name: 'Untitled Bench',
+
+ _estimate: function() {
+ var n = 1,
+ t = 0;
+
+ while (n < MAX_RUNS_ESTIMATE && (t = this._run(n)) < MIN_TIME_ESTIMATE)
+ t *= 10;
+
+ return { n: n, t: t };
+ },
+
+ _run: function(n) {
+ var fn = this.fn,
+ t = new Date;
+
+ while (n-- > 0) fn();
+
+ return new Date - t;
+ }
+});
View
7 perf/dom.observable.js
@@ -0,0 +1,7 @@
+var Bench = require('./bench'),
+ uki = require('uki-core'),
+ benches = exports.benches = [];
+
+benches[benches.length] = new Bench('Dom Observable', function() {
+
+});
View
13 perf/runner.css
@@ -0,0 +1,13 @@
+.runner .uki-nc-select {
+ vertical-align: middle;
+ margin-right: 0.5em;
+}
+
+.runner .uki-button {
+ vertical-align: middle;
+ margin-right: 0.5em;
+}
+
+.runner .uki-label {
+ vertical-align: middle;
+}
View
12 perf/runner.js
@@ -0,0 +1,12 @@
+var uki = require('uki');
+
+requireCss('./runner.css');
+
+var benches = []
+ .concat(require('./dom.observable').benches);
+
+uki({ view: 'Container', addClass: 'runner', childViews: [
+ { view: 'nativeControl.Select', options: uki.pluck(benches, 'name') },
+ { view: 'Button', label: 'Run' },
+ { view: 'Label', init: { tagName: 'span' }, text: 'Result: 0ms' }
+] }).attach();
View
2 src/uki-core.js
@@ -30,7 +30,7 @@ uki.version = '0.4.0a4';
// push everything into core namespace
utils.extend(uki,
- utils, builder, selector, collection,
+ env, utils, builder, selector, collection,
require('./uki-core/function'),
require('./uki-core/dom'),
require('./uki-core/event'),
View
25 src/uki-core/collection.js
@@ -145,20 +145,24 @@ utils.forEach([
Collection.addMethods = function(methods) {
utils.forEach(methods, function(name) {
- proto[name] = function() {
- for (var i = this.length - 1; i >= 0; i--) {
- this[i][name].apply(this[i], arguments);
- }
- return this;
- };
+ if (!proto[name]) {
+ proto[name] = function() {
+ for (var i = this.length - 1; i >= 0; i--) {
+ this[i][name].apply(this[i], arguments);
+ }
+ return this;
+ };
+ }
});
};
Collection.addProps = function(props) {
utils.forEach(props, function(name) {
- proto[name] = function(value) {
- return this.prop(name, value);
- };
+ if (!proto[name]) {
+ proto[name] = function(value) {
+ return this.prop(name, value);
+ };
+ }
});
};
@@ -170,7 +174,8 @@ Collection.addMethods([
]);
Collection.addProps([
- 'id', 'dom', 'text', 'html', 'pos', 'visible', 'style'
+ 'id', 'dom', 'text', 'html', 'pos', 'visible', 'style', 'binding',
+ 'bindings'
]);
View
2 src/uki-core/event.js
@@ -131,7 +131,7 @@ function wrapDomEvent(baseEvent) {
// This is expensive. I'd rather use much faster createEvent() here.
// Unfortunately firefox uses read only properties on native events,
// thus preventing modification even in descendants
- for (var i = eventProps.length, prop; i; i--) {
+ for (var i = eventProps.length, prop; i >= -1; i--) {
prop = eventProps[i];
e[prop] = baseEvent[prop];
}
View
86 src/uki-core/function.js
@@ -250,42 +250,6 @@ fun.delegateCall = function(source, name, target, targetName) {
}
};
-
-var afterBound = {},
- afterTimer = 0,
- afterQueue = [];
-
-fun.after = function(callback) {
- callback.huid = callback.huid || env.guid++;
- if (afterBound[callback.huid]) { return; }
- afterBound[callback.huid] = true;
- afterQueue.push(callback);
- scheduleAfterCallbacks();
-};
-
-function runAfterCallbacks() {
- clearAfterTimer();
- var queue = afterQueue;
- afterQueue = [];
- afterBound = {};
- for (var i = 0; i < queue.length; i++) {
- queue[i]();
- }
-};
-
-function scheduleAfterCallbacks() {
- if (afterTimer) { return; }
- afterTimer = setTimeout(runAfterCallbacks, 1);
-};
-
-function clearAfterTimer() {
- if (!afterTimer) { return; }
- clearTimeout(afterTimer);
- afterTimer = 0;
-};
-
-
-
function timer(fn, timeout, debounce) {
var running;
@@ -313,9 +277,53 @@ fun.debounce = function(fn, timeout) {
return timer(fn, timeout, true);
};
-fun.defer = function(fn, timeout) {
- timeout = timeout || 0;
- return setTimeout(fn, timeout);
+if (global.postMessage) {
+ var deferMessage = "uki-defer-" + env.expando,
+ listening = false,
+ deferQueue = [];
+
+ fun.defer = function(callback) {
+ if (!listening) {
+ global.addEventListener('message', function(e) {
+ if (e.data == deferMessage) {
+ e.stopPropagation();
+ var queue = deferQueue;
+ deferQueue = [];
+ utils.invoke(queue);
+ }
+ }, false);
+ listening = true;
+ }
+ deferQueue.push(callback);
+ global.postMessage(deferMessage, "*");
+ };
+} else {
+ fun.defer = function(callback) {
+ return global.setTimeout(callback, 0);
+ };
+}
+
+var deferOnceBound = {},
+ deferOnceQueue = [];
+
+fun.deferOnce = function(callback, context) {
+ if (context) {
+ callback = fun.bindOnce(callback, context);
+ }
+ callback.huid = callback.huid || env.guid++;
+ if (deferOnceBound[callback.huid]) { return; }
+ deferOnceBound[callback.huid] = true;
+ deferOnceQueue.push(callback);
+ if (deferOnceQueue.length === 1) {
+ fun.defer(runAfterCallbacks);
+ }
+};
+
+function runAfterCallbacks() {
+ var queue = deferOnceQueue;
+ deferOnceQueue = [];
+ deferOnceBound = {};
+ utils.invoke(queue);
};
View
21 src/uki-core/gesture.js
@@ -58,8 +58,19 @@ function stopGesture () {
evt.removeListener(env.doc, 'selectstart mousedown', evt.preventDefaultHandler);
}
+function addOffset(e) {
+ e.dragOffset = {
+ x: e.pageX - gesture.position.x,
+ y: e.pageY - gesture.position.y
+ };
+}
+
function dragGestureStart (e) {
e = evt.createEvent(e, { type: 'draggesturestart', simulatePropagation: true });
+ e.dragOffset = {
+ x: 0,
+ y: 0
+ };
evt.trigger(this, e);
if (!e.isDefaultPrevented()) {
gesture.position = { x: e.pageX, y: e.pageY };
@@ -69,21 +80,15 @@ function dragGestureStart (e) {
function dragGesture (e) {
e = evt.createEvent(e, { type: 'draggesture', simulatePropagation: true });
- e.dragOffset = {
- x: e.pageX - gesture.position.x,
- y: e.pageY - gesture.position.y
- };
+ addOffset(e);
evt.trigger(gesture.draggable, e);
if (e.isDefaultPrevented()) stopGesture(gesture.draggable);
}
function dragGestureEnd (e) {
e = evt.createEvent(e, { type: 'draggestureend', simulatePropagation: true });
- e.dragOffset = {
- x: e.pageX - gesture.position.x,
- y: e.pageY - gesture.position.y
- };
+ addOffset(e);
evt.trigger(gesture.draggable, e);
stopGesture(gesture.draggable);
View
13 src/uki-core/utils.js
@@ -86,10 +86,15 @@ utils.without = function(array, value) {
};
utils.invoke = function(array, method) {
- var args = slice.call(arguments, 1);
- function invoke(item) {
- return item[method].apply(item, args);
- }
+ var args = slice.call(arguments, 2),
+ invoke = method ?
+ function(item) {
+ return item[method].apply(item, args);
+ } :
+ function(item) {
+ return item.apply(null, args);
+ };
+
return array.forEach ?
array.forEach(invoke) :
utils.forEach(array, invoke);
View
14 src/uki-view/view/button.js
@@ -1,11 +1,11 @@
requireCss('./button/button.css');
-var fun = require('uki-core/function'),
- view = require('uki-core/view'),
- dom = require('uki-core/dom'),
+var fun = require('../../uki-core/function'),
+ view = require('../../uki-core/view'),
+ dom = require('../../uki-core/dom'),
- Base = require('uki-core/view/base').Base,
- Focusable = require('uki-core/view/focusable').Focusable;
+ Base = require('../../uki-core/view/base').Base,
+ Focusable = require('../../uki-core/view/focusable').Focusable;
var Button = view.newClass('Button', Base, Focusable, {
@@ -59,5 +59,7 @@ function updateImageOnly () {
this.toggleClass('uki-button_image-only', !!(this.iconSrc() && !this.labelHtml()));
}
-
+require('../../uki-core/collection').Collection.addProps([
+ 'labelHTML', 'label', 'disabled', 'confirm', 'iconSrc'
+]);
exports.Button = Button;
View
9 src/uki-view/view/button/button.css
@@ -1,18 +1,20 @@
.uki-button {
- border: 1px solid #777;
+ border: 1px solid #999;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
+ border-bottom-color: #777;
border-radius: 3px;
background: #D7DAE4 url(button.png) repeat-x 0 0;
padding: 4px 0;
white-space: nowrap;
- font-weight: bold;
vertical-align: top;
text-align: center;
font-size: 12px;
font-family: Arial, Helvetica, sans-serif;
- color: #333;
+ color: #111;
+ line-height: 12px;
+ text-shadow: rgba(255, 255, 255, 0.5) 0 1px 0px;
position: relative;
z-index: 10;
@@ -31,6 +33,7 @@
}
.uki-button:active {
+ border-color: #666;
background-color: #C5C7CD;
background-position: 0 -80px;
View
22 src/uki-view/view/dataList.js
@@ -1,18 +1,18 @@
requireCss('./dataList/dataList.css');
-var env = require('uki-core/env'),
- fun = require('uki-core/function'),
- utils = require('uki-core/utils'),
- dom = require('uki-core/dom'),
- evt = require('uki-core/event'),
- view = require('uki-core/view'),
- build = require('uki-core/builder').build,
+var env = require('../../uki-core/env'),
+ fun = require('../../uki-core/function'),
+ utils = require('../../uki-core/utils'),
+ dom = require('../../uki-core/dom'),
+ evt = require('../../uki-core/event'),
+ view = require('../../uki-core/view'),
+ build = require('../../uki-core/builder').build,
Binding = require('./dataList/binding').Binding,
- Mustache = require('uki-core/mustache').Mustache;
- Base = require('uki-core/view/base').Base;
- Focusable = require('uki-core/view/focusable').Focusable;
- Selectable = require('selectable').Selectable;
+ Mustache = require('../../uki-core/mustache').Mustache;
+ Base = require('../../uki-core/view/base').Base;
+ Focusable = require('../../uki-core/view/focusable').Focusable;
+ Selectable = require('./selectable').Selectable;
var DataList = view.newClass('DataList', Base, Focusable, Selectable, {}),
View
4 src/uki-view/view/dataList/binding.js
@@ -1,6 +1,6 @@
-var fun = require('uki-core/function'),
- BaseBinding = require('uki-core/binding').Binding;
+var fun = require('../../../uki-core/function'),
+ BaseBinding = require('../../../uki-core/binding').Binding;
var Binding = fun.newClass(BaseBinding, {
View
10 src/uki-view/view/flow.js
@@ -1,11 +1,11 @@
requireCss('./flow/flow.css');
-var fun = require('uki-core/function'),
- utils = require('uki-core/utils'),
- view = require('uki-core/view'),
- dom = require('uki-core/dom'),
+var fun = require('../../uki-core/function'),
+ utils = require('../../uki-core/utils'),
+ view = require('../../uki-core/view'),
+ dom = require('../../uki-core/dom'),
- Container = require('uki-core/view/container').Container;
+ Container = require('../../uki-core/view/container').Container;
var Flow = view.newClass('Flow', Container, {
View
22 src/uki-view/view/nativeControl.js
@@ -1,14 +1,14 @@
requireCss('./nativeControl/nativeControl.css');
-var fun = require('uki-core/function'),
- view = require('uki-core/view'),
- utils = require('uki-core/utils'),
- dom = require('uki-core/dom'),
- env = require('uki-core/env'),
- evt = require('uki-core/event'),
+var fun = require('../../uki-core/function'),
+ view = require('../../uki-core/view'),
+ utils = require('../../uki-core/utils'),
+ dom = require('../../uki-core/dom'),
+ env = require('../../uki-core/env'),
+ evt = require('../../uki-core/event'),
- Focusable = require('uki-core/view/focusable').Focusable,
- Base = require('uki-core/view/base').Base;
+ Focusable = require('../../uki-core/view/focusable').Focusable,
+ Base = require('../../uki-core/view/base').Base;
var ieResize = env.ua.match(/MSIE 6|7/);
@@ -190,6 +190,8 @@ var Select = view.newClass('nativeControl.Select', NativeControl, {
return this;
})
});
+fun.delegateProp(NativeControl.prototype,
+ ['selectedIndex'], '_input');
function appendOptions (root, options) {
var node;
@@ -214,6 +216,10 @@ function appendOptions (root, options) {
}
+require('../../uki-core/collection').Collection.addProps([
+ 'name', 'checked', 'disabled', 'value', 'type', 'placeholder',
+ 'disabled', 'options', 'selectedIndex'
+]);
exports.nativeControl = {
NativeControl: NativeControl,
Radio: Radio,
View
8 src/uki-view/view/selectable.js
@@ -1,7 +1,7 @@
-var env = require('uki-core/env'),
- fun = require('uki-core/function'),
- utils = require('uki-core/utils'),
- dom = require('uki-core/dom');
+var env = require('../../uki-core/env'),
+ fun = require('../../uki-core/function'),
+ utils = require('../../uki-core/utils'),
+ dom = require('../../uki-core/dom');
/**
View
31 src/uki-view/view/splitPane.js
@@ -1,15 +1,15 @@
requireCss('./splitPane/splitPane.css');
-var fun = require('uki-core/function'),
- utils = require('uki-core/utils'),
- view = require('uki-core/view'),
- evt = require('uki-core/event'),
- dom = require('uki-core/dom'),
- build = require('uki-core/builder').build,
+var fun = require('../../uki-core/function'),
+ utils = require('../../uki-core/utils'),
+ view = require('../../uki-core/view'),
+ evt = require('../../uki-core/event'),
+ dom = require('../../uki-core/dom'),
+ build = require('../../uki-core/builder').build,
- Mustache = require('uki-core/mustache').Mustache,
- Container = require('uki-core/view/container').Container,
- Focusable = require('uki-core/view/focusable').Focusable;
+ Mustache = require('../../uki-core/mustache').Mustache,
+ Container = require('../../uki-core/view/container').Container,
+ Focusable = require('../../uki-core/view/focusable').Focusable;
var SplitPane = view.newClass('SplitPane', Container, Focusable, {}),
@@ -178,11 +178,11 @@ proto._createDom = function() {
this._dom.appendChild(this._handle = this._createHandle());
};
-proto._scheduleChildResize = function() {
+proto._throttledChildResize = function() {
this._resizeChildViews();
};
-proto._resizeSelf = function() {
+proto.resized = function() {
this._moveHandle();
if (!this._prevWidth) {
@@ -192,8 +192,8 @@ proto._resizeSelf = function() {
} else {
this._handlePosition = this._normalizeHandlePosition(this._calcDesiredPosition());
this._moveHandle();
- this._scheduleChildResize();
}
+ this._throttledChildResize();
};
proto._calcDesiredPosition = function() {
@@ -208,6 +208,7 @@ proto._calcDesiredPosition = function() {
proto._draggesturestart = function(e) {
e.cursor = dom.computedStyle(this._handle, null).cursor;
this._positionBeforeDrag = this.handlePosition();
+ this._updatePositionOnDrag(e);
};
proto._draggesture = function(e) {
@@ -225,7 +226,7 @@ proto._updatePositionOnDrag = function(e, stop) {
var pos = this._positionBeforeDrag + e.dragOffset[this._x_xName()];
this._handlePosition = this._normalizeHandlePosition(pos);
this._moveHandle();
- this._scheduleChildResize();
+ this._throttledChildResize();
this.trigger({
type: stop ? 'handleStop' : 'handleMove',
@@ -286,4 +287,8 @@ proto._resizeChildViews = function() {
};
+require('../../uki-core/collection').Collection.addProps([
+ 'leftMin', 'rightMin', 'leftSpeed', 'rightSpeed', 'throttle',
+ 'handlePosition', 'extPositions', 'handleWidth', 'vertical'
+]);
exports.SplitPane = SplitPane;
View
12 src/uki-view/view/text.js
@@ -1,10 +1,10 @@
requireCss('./text/text.css');
-var fun = require('uki-core/function'),
- dom = require('uki-core/dom'),
- view = require('uki-core/view'),
+var fun = require('../../uki-core/function'),
+ dom = require('../../uki-core/dom'),
+ view = require('../../uki-core/view'),
- Base = require('uki-core/view/base').Base;
+ Base = require('../../uki-core/view/base').Base;
var Text = view.newClass('Text', Base, {
@@ -42,7 +42,9 @@ var Header = view.newClass('Header', Base, {
})
});
-
+require('../../uki-core/collection').Collection.addProps([
+ 'for'
+]);
exports.Text = Text;
exports.P = P;
exports.Label = Label;
View
2 src/uki.js
@@ -1,3 +1,3 @@
-module.exports = global.uki = require('uki-core');
+module.exports = require('uki-core');
require('uki-view');
module.exports.createStylesheet(__requiredCss);
View
6 test/function.test.js
@@ -259,14 +259,14 @@ test('delegateCall', 2, function() {
});
-module('after');
+module('deferOnce');
asyncTest('is called', 2, function() {
var count = 0;
function counter() {
count++;
}
- fun.after(counter);
+ fun.deferOnce(counter);
equal(count, 0);
setTimeout(function() {
equal(count, 1);
@@ -280,7 +280,7 @@ asyncTest('is de-duped', 2, function() {
count++;
}
for(var i = 0; i < 100; i++) {
- fun.after(counter);
+ fun.deferOnce(counter);
}
equal(count, 0);
setTimeout(function() {
View
24 tools/static_require.js
@@ -131,15 +131,23 @@ function addFileToAstList (filePath, wrap) {
var text = fs.readFileSync(filePath, 'utf8');
// remove shebang
text = text.replace(/^\#\!.*/, '');
- if (wrap) {
- text = '(function(global, module) {var exports = this;' + text + '})';
+ var ast;
+ if (text.indexOf('@static_require noprocess') === -1) {
+ if (wrap) {
+ text = '(function(global, module, require) {var exports = this;' + text + '})';
+ }
+ ast = jsp.parse(text);
+ ast = walker.with_walkers(walkers, function() {
+ return walker.walk(ast);
+ });
+ } else {
+ if (wrap) {
+ text = '(function() { var require = undefined;' + text + '})';
+ }
+ ast = jsp.parse(text);
}
- var ast = jsp.parse(text);
- var newAst = walker.with_walkers(walkers, function() {
- return walker.walk(ast);
- });
state.currentPath = oldPath;
- state.requiredAsts[state.required[filePath]] = newAst;
+ state.requiredAsts[state.required[filePath]] = ast;
}
function staticRequire (filePath, options) {
@@ -163,7 +171,7 @@ function staticRequire (filePath, options) {
addFileToAstList(filePath, true);
var code = 'var global = this;';
- code += 'function require(index) { if (!require.cache[index]) {var module = require.cache[index] = {exports: {}}; require.modules[index].call(module.exports, global, module);} return require.cache[index].exports; }\n';
+ code += 'function require(index) { if (!require.cache[index]) {var module = require.cache[index] = {exports: {}}; require.modules[index].call(module.exports, global, module, require);} return require.cache[index].exports; }\n';
code += 'var require_modules = require.modules = []; require.cache = [];';
var body = jsp.parse(code)[1];

0 comments on commit ccc054b

Please sign in to comment.