A piano to hack on while going to/from work.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

396 lines
15 KiB

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
var root = (objectTypes[typeof window] && window) || this,
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx.binding', 'exports'], function (Rx, exports) {
root.Rx = factory(root, exports, Rx);
return root.Rx;
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
// Aliases
var Observable = Rx.Observable,
observableProto = Observable.prototype,
observableFromPromise = Observable.fromPromise,
observableThrow = Observable.throwException,
AnonymousObservable = Rx.AnonymousObservable,
AsyncSubject = Rx.AsyncSubject,
disposableCreate = Rx.Disposable.create,
CompositeDisposable= Rx.CompositeDisposable,
immediateScheduler = Rx.Scheduler.immediate,
timeoutScheduler = Rx.Scheduler.timeout,
slice = Array.prototype.slice;
/**
* Invokes the specified function asynchronously on the specified scheduler, surfacing the result through an observable sequence.
*
* @example
* var res = Rx.Observable.start(function () { console.log('hello'); });
* var res = Rx.Observable.start(function () { console.log('hello'); }, Rx.Scheduler.timeout);
* var res = Rx.Observable.start(function () { this.log('hello'); }, Rx.Scheduler.timeout, console);
*
* @param {Function} func Function to run asynchronously.
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @returns {Observable} An observable sequence exposing the function's result value, or an exception.
*
* Remarks
* * The function is called immediately, not during the subscription of the resulting sequence.
* * Multiple subscriptions to the resulting sequence can observe the function's result.
*/
Observable.start = function (func, scheduler, context) {
return observableToAsync(func, scheduler, context)();
};
/**
* Converts the function into an asynchronous function. Each invocation of the resulting asynchronous function causes an invocation of the original synchronous function on the specified scheduler.
*
* @example
* var res = Rx.Observable.toAsync(function (x, y) { return x + y; })(4, 3);
* var res = Rx.Observable.toAsync(function (x, y) { return x + y; }, Rx.Scheduler.timeout)(4, 3);
* var res = Rx.Observable.toAsync(function (x) { this.log(x); }, Rx.Scheduler.timeout, console)('hello');
*
* @param {Function} function Function to convert to an asynchronous function.
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @returns {Function} Asynchronous function.
*/
var observableToAsync = Observable.toAsync = function (func, scheduler, context) {
scheduler || (scheduler = timeoutScheduler);
return function () {
var args = arguments,
subject = new AsyncSubject();
scheduler.schedule(function () {
var result;
try {
result = func.apply(context, args);
} catch (e) {
subject.onError(e);
return;
}
subject.onNext(result);
subject.onCompleted();
});
return subject.asObservable();
};
};
/**
* Converts a callback function to an observable sequence.
*
* @param {Function} function Function with a callback as the last parameter to convert to an Observable sequence.
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @param {Function} [selector] A selector which takes the arguments from the callback to produce a single item to yield on next.
* @returns {Function} A function, when executed with the required parameters minus the callback, produces an Observable sequence with a single value of the arguments to the callback as an array.
*/
Observable.fromCallback = function (func, scheduler, context, selector) {
scheduler || (scheduler = immediateScheduler);
return function () {
var args = slice.call(arguments, 0);
return new AnonymousObservable(function (observer) {
return scheduler.schedule(function () {
function handler(e) {
var results = e;
if (selector) {
try {
results = selector(arguments);
} catch (err) {
observer.onError(err);
return;
}
} else {
if (results.length === 1) {
results = results[0];
}
}
observer.onNext(results);
observer.onCompleted();
}
args.push(handler);
func.apply(context, args);
});
});
};
};
/**
* Converts a Node.js callback style function to an observable sequence. This must be in function (err, ...) format.
* @param {Function} func The function to call
* @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
* @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
* @param {Function} [selector] A selector which takes the arguments from the callback minus the error to produce a single item to yield on next.
* @returns {Function} An async function which when applied, returns an observable sequence with the callback arguments as an array.
*/
Observable.fromNodeCallback = function (func, scheduler, context, selector) {
scheduler || (scheduler = immediateScheduler);
return function () {
var args = slice.call(arguments, 0);
return new AnonymousObservable(function (observer) {
return scheduler.schedule(function () {
function handler(err) {
if (err) {
observer.onError(err);
return;
}
var results = slice.call(arguments, 1);
if (selector) {
try {
results = selector(results);
} catch (e) {
observer.onError(e);
return;
}
} else {
if (results.length === 1) {
results = results[0];
}
}
observer.onNext(results);
observer.onCompleted();
}
args.push(handler);
func.apply(context, args);
});
});
};
};
function fixEvent(event) {
var stopPropagation = function () {
this.cancelBubble = true;
};
var preventDefault = function () {
this.bubbledKeyCode = this.keyCode;
if (this.ctrlKey) {
try {
this.keyCode = 0;
} catch (e) { }
}
this.defaultPrevented = true;
this.returnValue = false;
this.modified = true;
};
event || (event = root.event);
if (!event.target) {
event.target = event.target || event.srcElement;
if (event.type == 'mouseover') {
event.relatedTarget = event.fromElement;
}
if (event.type == 'mouseout') {
event.relatedTarget = event.toElement;
}
// Adding stopPropogation and preventDefault to IE
if (!event.stopPropagation){
event.stopPropagation = stopPropagation;
event.preventDefault = preventDefault;
}
// Normalize key events
switch(event.type){
case 'keypress':
var c = ('charCode' in event ? event.charCode : event.keyCode);
if (c == 10) {
c = 0;
event.keyCode = 13;
} else if (c == 13 || c == 27) {
c = 0;
} else if (c == 3) {
c = 99;
}
event.charCode = c;
event.keyChar = event.charCode ? String.fromCharCode(event.charCode) : '';
break;
}
}
return event;
}
function createListener (element, name, handler) {
// Node.js specific
if (element.addListener) {
element.addListener(name, handler);
return disposableCreate(function () {
element.removeListener(name, handler);
});
}
// Standards compliant
if (element.addEventListener) {
element.addEventListener(name, handler, false);
return disposableCreate(function () {
element.removeEventListener(name, handler, false);
});
}
if (element.attachEvent) {
// IE Specific
var innerHandler = function (event) {
handler(fixEvent(event));
};
element.attachEvent('on' + name, innerHandler);
return disposableCreate(function () {
element.detachEvent('on' + name, innerHandler);
});
}
// Level 1 DOM Events
element['on' + name] = handler;
return disposableCreate(function () {
element['on' + name] = null;
});
}
function createEventListener (el, eventName, handler) {
var disposables = new CompositeDisposable();
// Asume NodeList
if (typeof el.item === 'function' && typeof el.length === 'number') {
for (var i = 0, len = el.length; i < len; i++) {
disposables.add(createEventListener(el.item(i), eventName, handler));
}
} else if (el) {
disposables.add(createListener(el, eventName, handler));
}
return disposables;
}
// Check for Angular/jQuery/Zepto support
var jq =
!!root.angular && !!angular.element ? angular.element :
(!!root.jQuery ? root.jQuery : (
!!root.Zepto ? root.Zepto : null));
// Check for ember
var ember = !!root.Ember && typeof root.Ember.addListener === 'function';
/**
* Creates an observable sequence by adding an event listener to the matching DOMElement or each item in the NodeList.
*
* @example
* var source = Rx.Observable.fromEvent(element, 'mouseup');
*
* @param {Object} element The DOMElement or NodeList to attach a listener.
* @param {String} eventName The event name to attach the observable sequence.
* @param {Function} [selector] A selector which takes the arguments from the event handler to produce a single item to yield on next.
* @returns {Observable} An observable sequence of events from the specified element and the specified event.
*/
Observable.fromEvent = function (element, eventName, selector) {
if (ember) {
return fromEventPattern(
function (h) { Ember.addListener(element, eventName); },
function (h) { Ember.removeListener(element, eventName); },
selector);
}
if (jq) {
var $elem = jq(element);
return fromEventPattern(
function (h) { $elem.on(eventName, h); },
function (h) { $elem.off(eventName, h); },
selector);
}
return new AnonymousObservable(function (observer) {
return createEventListener(
element,
eventName,
function handler (e) {
var results = e;
if (selector) {
try {
results = selector(arguments);
} catch (err) {
observer.onError(err);
return
}
}
observer.onNext(results);
});
}).publish().refCount();
};
/**
* Creates an observable sequence from an event emitter via an addHandler/removeHandler pair.
* @param {Function} addHandler The function to add a handler to the emitter.
* @param {Function} [removeHandler] The optional function to remove a handler from an emitter.
* @param {Function} [selector] A selector which takes the arguments from the event handler to produce a single item to yield on next.
* @returns {Observable} An observable sequence which wraps an event from an event emitter
*/
var fromEventPattern = Observable.fromEventPattern = function (addHandler, removeHandler, selector) {
return new AnonymousObservable(function (observer) {
function innerHandler (e) {
var result = e;
if (selector) {
try {
result = selector(arguments);
} catch (err) {
observer.onError(err);
return;
}
}
observer.onNext(result);
}
var returnValue = addHandler(innerHandler);
return disposableCreate(function () {
if (removeHandler) {
removeHandler(innerHandler, returnValue);
}
});
}).publish().refCount();
};
/**
* Invokes the asynchronous function, surfacing the result through an observable sequence.
* @param {Function} functionAsync Asynchronous function which returns a Promise to run.
* @returns {Observable} An observable sequence exposing the function's result value, or an exception.
*/
Observable.startAsync = function (functionAsync) {
var promise;
try {
promise = functionAsync();
} catch (e) {
return observableThrow(e);
}
return observableFromPromise(promise);
}
return Rx;
}));