Alice
view release on metacpan or search on metacpan
share/static/alice.js view on Meta::CPAN
isElement: isElement,
isArray: isArray,
isHash: isHash,
isFunction: isFunction,
isString: isString,
isNumber: isNumber,
isDate: isDate,
isUndefined: isUndefined
});
})();
Object.extend(Function.prototype, (function() {
var slice = Array.prototype.slice;
function update(array, args) {
var arrayLength = array.length, length = args.length;
while (length--) array[arrayLength + length] = args[length];
return array;
}
function merge(array, args) {
array = slice.call(array, 0);
return update(array, args);
}
function argumentNames() {
var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
.replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
}
function bind(context) {
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
var __method = this, args = slice.call(arguments, 1);
return function() {
var a = merge(args, arguments);
return __method.apply(context, a);
}
}
function bindAsEventListener(context) {
var __method = this, args = slice.call(arguments, 1);
return function(event) {
var a = update([event || window.event], args);
return __method.apply(context, a);
}
}
function curry() {
if (!arguments.length) return this;
var __method = this, args = slice.call(arguments, 0);
return function() {
var a = merge(args, arguments);
return __method.apply(this, a);
}
}
function delay(timeout) {
var __method = this, args = slice.call(arguments, 1);
timeout = timeout * 1000;
return window.setTimeout(function() {
return __method.apply(__method, args);
}, timeout);
}
function defer() {
var args = update([0.01], arguments);
return this.delay.apply(this, args);
}
function wrap(wrapper) {
var __method = this;
return function() {
var a = update([__method.bind(this)], arguments);
return wrapper.apply(this, a);
}
}
function methodize() {
if (this._methodized) return this._methodized;
var __method = this;
return this._methodized = function() {
var a = update([this], arguments);
return __method.apply(null, a);
};
}
return {
argumentNames: argumentNames,
bind: bind,
bindAsEventListener: bindAsEventListener,
curry: curry,
delay: delay,
defer: defer,
wrap: wrap,
methodize: methodize
}
})());
(function(proto) {
function toISOString() {
return this.getUTCFullYear() + '-' +
(this.getUTCMonth() + 1).toPaddedString(2) + '-' +
this.getUTCDate().toPaddedString(2) + 'T' +
this.getUTCHours().toPaddedString(2) + ':' +
this.getUTCMinutes().toPaddedString(2) + ':' +
this.getUTCSeconds().toPaddedString(2) + 'Z';
}
function toJSON() {
return this.toISOString();
}
if (!proto.toISOString) proto.toISOString = toISOString;
if (!proto.toJSON) proto.toJSON = toJSON;
share/static/alice.js view on Meta::CPAN
});
Ajax.Updater = Class.create(Ajax.Request, {
initialize: function($super, container, url, options) {
this.container = {
success: (container.success || container),
failure: (container.failure || (container.success ? null : container))
};
options = Object.clone(options);
var onComplete = options.onComplete;
options.onComplete = (function(response, json) {
this.updateContent(response.responseText);
if (Object.isFunction(onComplete)) onComplete(response, json);
}).bind(this);
$super(url, options);
},
updateContent: function(responseText) {
var receiver = this.container[this.success() ? 'success' : 'failure'],
options = this.options;
if (!options.evalScripts) responseText = responseText.stripScripts();
if (receiver = $(receiver)) {
if (options.insertion) {
if (Object.isString(options.insertion)) {
var insertion = { }; insertion[options.insertion] = responseText;
receiver.insert(insertion);
}
else options.insertion(receiver, responseText);
}
else receiver.update(responseText);
}
}
});
Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
initialize: function($super, container, url, options) {
$super(options);
this.onComplete = this.options.onComplete;
this.frequency = (this.options.frequency || 2);
this.decay = (this.options.decay || 1);
this.updater = { };
this.container = container;
this.url = url;
this.start();
},
start: function() {
this.options.onComplete = this.updateComplete.bind(this);
this.onTimerEvent();
},
stop: function() {
this.updater.options.onComplete = undefined;
clearTimeout(this.timer);
(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
},
updateComplete: function(response) {
if (this.options.decay) {
this.decay = (response.responseText == this.lastText ?
this.decay * this.options.decay : 1);
this.lastText = response.responseText;
}
this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
},
onTimerEvent: function() {
this.updater = new Ajax.Updater(this.container, this.url, this.options);
}
});
function $(element) {
if (arguments.length > 1) {
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
elements.push($(arguments[i]));
return elements;
}
if (Object.isString(element))
element = document.getElementById(element);
return Element.extend(element);
}
if (Prototype.BrowserFeatures.XPath) {
document._getElementsByXPath = function(expression, parentElement) {
var results = [];
var query = document.evaluate(expression, $(parentElement) || document,
null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0, length = query.snapshotLength; i < length; i++)
results.push(Element.extend(query.snapshotItem(i)));
return results;
};
}
/*--------------------------------------------------------------------------*/
if (!Node) var Node = { };
if (!Node.ELEMENT_NODE) {
Object.extend(Node, {
ELEMENT_NODE: 1,
ATTRIBUTE_NODE: 2,
TEXT_NODE: 3,
CDATA_SECTION_NODE: 4,
ENTITY_REFERENCE_NODE: 5,
ENTITY_NODE: 6,
PROCESSING_INSTRUCTION_NODE: 7,
COMMENT_NODE: 8,
DOCUMENT_NODE: 9,
DOCUMENT_TYPE_NODE: 10,
DOCUMENT_FRAGMENT_NODE: 11,
NOTATION_NODE: 12
});
share/static/alice.js view on Meta::CPAN
},
handleEvent: function(event) {
var element = Event.findElement(event, this.selector);
if (element) this.callback.call(this.element, event, element);
}
});
function on(element, eventName, selector, callback) {
element = $(element);
if (Object.isFunction(selector) && Object.isUndefined(callback)) {
callback = selector, selector = null;
}
return new Event.Handler(element, eventName, selector, callback).start();
}
Object.extend(Event, Event.Methods);
Object.extend(Event, {
fire: fire,
observe: observe,
stopObserving: stopObserving,
on: on
});
Element.addMethods({
fire: fire,
observe: observe,
stopObserving: stopObserving,
on: on
});
Object.extend(document, {
fire: fire.methodize(),
observe: observe.methodize(),
stopObserving: stopObserving.methodize(),
on: on.methodize(),
loaded: false
});
if (window.Event) Object.extend(window.Event, Event);
else window.Event = Event;
})();
(function() {
/* Support for the DOMContentLoaded event is based on work by Dan Webb,
Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
var timer;
function fireContentLoadedEvent() {
if (document.loaded) return;
if (timer) window.clearTimeout(timer);
document.loaded = true;
document.fire('dom:loaded');
}
function checkReadyState() {
if (document.readyState === 'complete') {
document.stopObserving('readystatechange', checkReadyState);
fireContentLoadedEvent();
}
}
function pollDoScroll() {
try { document.documentElement.doScroll('left'); }
catch(e) {
timer = pollDoScroll.defer();
return;
}
fireContentLoadedEvent();
}
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
} else {
document.observe('readystatechange', checkReadyState);
if (window == top)
timer = pollDoScroll.defer();
}
Event.observe(window, 'load', fireContentLoadedEvent);
})();
Element.addMethods();
/*------------------------------- DEPRECATED -------------------------------*/
Hash.toQueryString = Object.toQueryString;
var Toggle = { display: Element.toggle };
Element.Methods.childOf = Element.Methods.descendantOf;
var Insertion = {
Before: function(element, content) {
return Element.insert(element, {before:content});
},
Top: function(element, content) {
return Element.insert(element, {top:content});
},
Bottom: function(element, content) {
return Element.insert(element, {bottom:content});
},
After: function(element, content) {
return Element.insert(element, {after:content});
}
};
var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
share/static/alice.js view on Meta::CPAN
});
if(affected.length>0)
drop = Droppables.findDeepestChild(affected);
if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
if (drop) {
Position.within(drop.element, point[0], point[1]);
if(drop.onHover)
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
if (drop != this.last_active) Droppables.activate(drop);
}
},
fire: function(event, element) {
if(!this.last_active) return;
Position.prepare();
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
if (this.last_active.onDrop) {
this.last_active.onDrop(element, this.last_active.element, event);
return true;
}
},
reset: function() {
if(this.last_active)
this.deactivate(this.last_active);
}
};
var Draggables = {
drags: [],
observers: [],
register: function(draggable) {
if(this.drags.length == 0) {
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
this.eventKeypress = this.keyPress.bindAsEventListener(this);
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
Event.observe(document, "keypress", this.eventKeypress);
}
this.drags.push(draggable);
},
unregister: function(draggable) {
this.drags = this.drags.reject(function(d) { return d==draggable });
if(this.drags.length == 0) {
Event.stopObserving(document, "mouseup", this.eventMouseUp);
Event.stopObserving(document, "mousemove", this.eventMouseMove);
Event.stopObserving(document, "keypress", this.eventKeypress);
}
},
activate: function(draggable) {
if(draggable.options.delay) {
this._timeout = setTimeout(function() {
Draggables._timeout = null;
window.focus();
Draggables.activeDraggable = draggable;
}.bind(this), draggable.options.delay);
} else {
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
this.activeDraggable = draggable;
}
},
deactivate: function() {
this.activeDraggable = null;
},
updateDrag: function(event) {
if(!this.activeDraggable) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
this._lastPointer = pointer;
this.activeDraggable.updateDrag(event, pointer);
},
endDrag: function(event) {
if(this._timeout) {
clearTimeout(this._timeout);
this._timeout = null;
}
if(!this.activeDraggable) return;
this._lastPointer = null;
this.activeDraggable.endDrag(event);
this.activeDraggable = null;
},
keyPress: function(event) {
if(this.activeDraggable)
this.activeDraggable.keyPress(event);
},
addObserver: function(observer) {
this.observers.push(observer);
this._cacheObserverCallbacks();
},
removeObserver: function(element) { // element instead of observer fixes mem leaks
this.observers = this.observers.reject( function(o) { return o.element==element });
this._cacheObserverCallbacks();
},
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
if(this[eventName+'Count'] > 0)
this.observers.each( function(o) {
if(o[eventName]) o[eventName](eventName, draggable, event);
});
if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
},
_cacheObserverCallbacks: function() {
['onStart','onEnd','onDrag'].each( function(eventName) {
Draggables[eventName+'Count'] = Draggables.observers.select(
function(o) { return o[eventName]; }
).length;
});
}
};
/*--------------------------------------------------------------------------*/
var Draggable = Class.create({
initialize: function(element) {
var defaults = {
handle: false,
reverteffect: function(element, top_offset, left_offset) {
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
queue: {scope:'_draggable', position:'end'}
});
},
endeffect: function(element) {
var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
queue: {scope:'_draggable', position:'end'},
afterFinish: function(){
Draggable._dragging[element] = false
}
});
share/static/alice.js view on Meta::CPAN
}
function observeButtonClick(element, handler) {
element.on('click', function(event) {
handler(this.editor);
event.stop();
}.bind(this));
}
function buttonStateHandler(name, options) {
if (options.query)
return options.query;
else if (options.get('query'))
return options.get('query');
else
return function(editor) { return editor.queryCommandState(name); };
}
function observeStateChanges(element, name, handler) {
var previousState;
this.editor.on("selection:change", function(event) {
var state = handler(this.editor);
if (state != previousState) {
previousState = state;
this.updateButtonState(element, name, state);
}
}.bind(this));
}
function updateButtonState(element, name, state) {
if (state)
element.addClassName('selected');
else
element.removeClassName('selected');
}
return {
initialize: initialize,
createToolbarElement: createToolbarElement,
addButtonSet: addButtonSet,
addButton: addButton,
createButtonElement: createButtonElement,
buttonHandler: buttonHandler,
observeButtonClick: observeButtonClick,
buttonStateHandler: buttonStateHandler,
observeStateChanges: observeStateChanges,
updateButtonState: updateButtonState
};
})());
WysiHat.Toolbar.ButtonSets = {};
WysiHat.Toolbar.ButtonSets.Basic = $A([
{ label: "Bold" },
{ label: "Underline" },
{ label: "Italic" }
]);
/* SWFObject v2.2 <http://code.google.com/p/swfobject/>
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,...
(function() {
if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) return;
var logger;
if (window.WEB_SOCKET_LOGGER) {
logger = WEB_SOCKET_LOGGER;
} else if (window.console && window.console.log && window.console.error) {
logger = window.console;
} else {
logger = {log: function(){ }, error: function(){ }};
}
if (swfobject.getFlashPlayerVersion().major < 10) {
logger.error("Flash Player >= 10.0.0 is required.");
return;
}
if (location.protocol == "file:") {
logger.error(
"WARNING: web-socket-js doesn't work in file:///... URL " +
"unless you set Flash Security Settings properly. " +
"Open the page via Web server i.e. http://...");
}
/**
* This class represents a faux web socket.
* @param {string} url
* @param {array or string} protocols
* @param {string} proxyHost
* @param {int} proxyPort
* @param {string} headers
*/
WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
var self = this;
self.__id = WebSocket.__nextId++;
WebSocket.__instances[self.__id] = self;
self.readyState = WebSocket.CONNECTING;
self.bufferedAmount = 0;
self.__events = {};
if (!protocols) {
protocols = [];
} else if (typeof protocols == "string") {
protocols = [protocols];
}
self.__createTask = setTimeout(function() {
WebSocket.__addTask(function() {
self.__createTask = null;
WebSocket.__flash.create(
self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
});
}, 0);
};
/**
* Send data to the web socket.
* @param {string} data The data to send to the socket.
* @return {boolean} True for success, false for failure.
*/
WebSocket.prototype.send = function(data) {
if (this.readyState == WebSocket.CONNECTING) {
throw "INVALID_STATE_ERR: Web Socket connection has not been established";
}
var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
if (result < 0) { // success
return true;
} else {
this.bufferedAmount += result;
return false;
}
};
/**
* Close this web socket gracefully.
*/
WebSocket.prototype.close = function() {
if (this.__createTask) {
clearTimeout(this.__createTask);
this.__createTask = null;
this.readyState = WebSocket.CLOSED;
return;
}
if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
return;
}
this.readyState = WebSocket.CLOSING;
WebSocket.__flash.close(this.__id);
};
/**
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
*
* @param {string} type
* @param {function} listener
* @param {boolean} useCapture
* @return void
*/
WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
if (!(type in this.__events)) {
this.__events[type] = [];
}
this.__events[type].push(listener);
};
/**
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
*
* @param {string} type
* @param {function} listener
* @param {boolean} useCapture
* @return void
*/
WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
if (!(type in this.__events)) return;
var events = this.__events[type];
for (var i = events.length - 1; i >= 0; --i) {
if (events[i] === listener) {
events.splice(i, 1);
break;
}
}
};
/**
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
*
* @param {Event} event
* @return void
*/
WebSocket.prototype.dispatchEvent = function(event) {
var events = this.__events[event.type] || [];
for (var i = 0; i < events.length; ++i) {
events[i](event);
}
var handler = this["on" + event.type];
if (handler) handler.apply(this, [event]);
};
share/static/alice.js view on Meta::CPAN
*/
WebSocket.__initialize = function() {
if (WebSocket.__flash) return;
if (WebSocket.__swfLocation) {
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
}
if (!window.WEB_SOCKET_SWF_LOCATION) {
logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
return;
}
if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
!WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
var swfHost = RegExp.$1;
if (location.host != swfHost) {
logger.error(
"[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
"('" + location.host + "' != '" + swfHost + "'). " +
"See also 'How to host HTML file and SWF file in different domains' section " +
"in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
"by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
}
}
var container = document.createElement("div");
container.id = "webSocketContainer";
container.style.position = "absolute";
if (WebSocket.__isFlashLite()) {
container.style.left = "0px";
container.style.top = "0px";
} else {
container.style.left = "-100px";
container.style.top = "-100px";
}
var holder = document.createElement("div");
holder.id = "webSocketFlash";
container.appendChild(holder);
document.body.appendChild(container);
swfobject.embedSWF(
WEB_SOCKET_SWF_LOCATION,
"webSocketFlash",
"1" /* width */,
"1" /* height */,
"10.0.0" /* SWF version */,
null,
null,
{hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
null,
function(e) {
if (!e.success) {
logger.error("[WebSocket] swfobject.embedSWF failed");
}
});
};
/**
* Called by Flash to notify JS that it's fully loaded and ready
* for communication.
*/
WebSocket.__onFlashInitialized = function() {
setTimeout(function() {
WebSocket.__flash = document.getElementById("webSocketFlash");
WebSocket.__flash.setCallerUrl(location.href);
WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
for (var i = 0; i < WebSocket.__tasks.length; ++i) {
WebSocket.__tasks[i]();
}
WebSocket.__tasks = [];
}, 0);
};
/**
* Called by Flash to notify WebSockets events are fired.
*/
WebSocket.__onFlashEvent = function() {
setTimeout(function() {
try {
var events = WebSocket.__flash.receiveEvents();
for (var i = 0; i < events.length; ++i) {
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
}
} catch (e) {
logger.error(e);
}
}, 0);
return true;
};
WebSocket.__log = function(message) {
logger.log(decodeURIComponent(message));
};
WebSocket.__error = function(message) {
logger.error(decodeURIComponent(message));
};
WebSocket.__addTask = function(task) {
if (WebSocket.__flash) {
task();
} else {
WebSocket.__tasks.push(task);
}
};
/**
* Test if the browser is running flash lite.
* @return {boolean} True if flash lite is running, false otherwise.
*/
WebSocket.__isFlashLite = function() {
if (!window.navigator || !window.navigator.mimeTypes) {
return false;
}
var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
return false;
}
return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
};
if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
if (window.addEventListener) {
window.addEventListener("load", function(){
WebSocket.__initialize();
}, false);
} else {
window.attachEvent("onload", function(){
WebSocket.__initialize();
});
}
}
})();
var Alice = { };
Object.extend(Alice, {
share/static/alice.js view on Meta::CPAN
node.cleanWhitespace();
},
epochToLocal: function(epoch, format) {
var date = new Date(parseInt(epoch) * 1000);
if (!date) return epoch;
var hours = date.getHours();
if (format == "12") {
var ap;
if (hours >= 12) {
if (hours > 12) hours -= 12;
ap = "p";
} else {
ap = "a"
}
return sprintf("%d:%02d%s", hours, date.getMinutes(), ap);
}
return sprintf("%02d:%02d", hours, date.getMinutes());
},
makeLinksClickable: function(elem) {
var children = elem.childNodes;
var length = children.length;
for (var i=0; i < length; i++) {
var node = children[i];
if (node.nodeName != "#text") {
Alice.makeLinksClickable(node);
}
else if (node.nodeValue.match(Alice.RE.url)) {
var span = new Element("SPAN");
span.innerHTML = node.nodeValue.escapeHTML().replace(
Alice.RE.url, '<a href="$1" target="_blank" rel="noreferrer">$1</a>');
node.parentNode.replaceChild(span, node);
}
}
},
growlNotify: function(message) {
if (window.fluid) {
window.fluid.showGrowlNotification({
title: message.subject,
description: message.body,
priority: 1,
sticky: false,
identifier: message.msgid
});
}
else if (window.webkitNotifications) {
if (window.webkitNotifications.checkPermission() == 0) {
var popup = window.webkitNotifications.createNotification(
"http://static.usealice.org/image/alice.png",
message.subject,
message.body
);
popup.ondisplay = function() {
setTimeout(function () {popup.cancel();}, 5000);
};
popup.show();
}
}
},
isSpecialKey: function(keyCode) {
var special_keys = [
16,27,9,32,13,8,145,20,144,19,45,36,46,35,33,34,37,38,39,
40,17,18,91,112,113,114,115,116,117,118,119,120,121,122,123,
224
];
return special_keys.indexOf(keyCode) > -1;
},
playAudio: function(image, audio) {
image.src = '/static/image/pause.png';
if (! audio) {
var url = image.nextSibling.href;
audio = new Audio(url);
audio.addEventListener('ended', function () {
image.src = '/static/image/play.png';
image.onclick = function () { Alice.playAudio(image, audio) };
});
}
audio.play();
image.onclick = function() {
audio.pause();
this.src = '/static/image/play.png';
this.onclick = function () { Alice.playAudio(this, audio) };
};
},
joinChannel: function() {
var network = $('join_network').value;
var channel = $('join_channel').value;
if (!network || !channel) {
alert("Must select a channel and network!");
return;
}
var win = alice.activeWindow();
alice.connection.sendMessage({
source: win.id,
msg: "/join -"+network+" "+channel
});
alice.input.disabled = false;
$('join').remove();
},
tabsets: {
addSet: function () {
var name = prompt("Please enter a name for this tab set.");
if (name && !Alice.tabsets.hasTabset(name)) {
Alice.tabsets.clearActive();
$('sets').insert('<li class="active">'+name.escapeHTML()+'</li>');
var list = $('empty_tabset').clone(true).addClassName('active').show();
list.id = null;
$('tabset_data').insert(list);
}
share/static/alice.js view on Meta::CPAN
this.tabs = $('tabs');
this.topic = $('topic');
this.nicklist = $('nicklist');
this.overlayVisible = false;
this.lastnotify = 0;
this.topic_height = "14px";
this.beep = new Audio("/static/beep.mp3");
this.oembeds = [];
this.jsonp_callbacks = {};
this.connection = window.WebSocket && !window.location.search.match(/&?stream=xhr/) ?
new Alice.Connection.WebSocket(this)
: new Alice.Connection.XHR(this);
this.tabs_width = $('tabs_container').getWidth();
this.tabs_layout = this.tabs.getLayout();
this.base_filters = this.baseFilters();
this.message_filters = [];
this.supportsTouch = 'createTouch' in document;
this.isPhone = window.navigator.userAgent.match(/(android|iphone|wosbrowser)/i) ? true : false;
this.isMobile = this.isPhone || Prototype.Browser.MobileSafari;
this.loadDelay = this.isMobile ? 3000 : 1000;
if (window.navigator.standalone || window.navigator.userAgent.match(/Fluid/)) this.loadDelay = 0;
this.keyboard = new Alice.Keyboard(this);
this.input = new Alice.Input(this, "msg");
this.submit = $("submit");
this.submit.observe("click", function (e) {
this.input.send(); e.stop()}.bind(this));
this.tabs.observe("webkitTransitionEnd", this.shiftEnd.bind(this));
this.tabs.observe("transitionend", this.shiftEnd.bind(this));
this.makeSortable();
this.setupTopic();
this.setupNicklist();
this.setupMenus();
},
getBacklog: function (win, max, limit) {
this.connection.requestChunk(win.id, limit, max);
},
fetchOembeds: function(cb) {
var req = new XMLHttpRequest();
req.open("GET", "https://noembed.com/providers");
req.onreadystatechange = function(){
if (req.readyState == 4) {
try {
var providers = req.responseText.evalJSON();
this.oembeds = providers.inject([], function(acc, site){
return acc.concat(site.patterns.map(function(pat){return new RegExp(pat)}));
});
} catch (e) {}
setTimeout(this.fetchOembeds.bind(this), 1000 * 60 * 5);
if (cb) cb();
}
}.bind(this);
req.send();
},
embed: function(a, win) {
var params = {
url: a.href,
maxheight: 300,
};
var req = new XMLHttpRequest();
req.open("GET", "https://www.noembed.com/embed?" + Object.toQueryString(params));
req.onreadystatechange = function(){
if (req.readyState == 4) {
var data = req.responseText.evalJSON();
this.embedContent(a, win, data);
}
}.bind(this);
req.send();
},
embedContent: function(a, win, data) {
if (!data || !data.html) return;
var html = data.html;
var elem = new Element("DIV", {"class": "oembed"});
if (data.provider_name == "Twitter") {
var position = win.captureScrollPosition();
elem.setStyle({display: "block"});
elem.update(html);
a.replace(elem);
win.scrollToPosition(position);
Alice.makeLinksClickable(elem);
return;
}
var position = win.captureScrollPosition();
a.update(data.title);
a.insert({
after: '<sup class="external"><a target="_blank" href="'+data.url+'">'
+data.provider_name+'</a></sup>'
});
a.up("div.msg").insert(elem);
win.scrollToPosition(position);
a.observe('click', function(e) {
e.stop();
var position = win.captureScrollPosition();
if (elem.innerHTML) {
elem.innerHTML = "";
elem.style.display = "none";
return;
}
elem.style.display = "block";
elem.innerHTML = html;
Alice.makeLinksClickable(elem);
var images = elem.select("img");
if (images.length) {
share/static/alice.js view on Meta::CPAN
if (!win.active) win.focus();
return true;
}
}
}
return false;
},
tabShift: function() {
return this.tabs_layout.get('left');
},
tabsWidth: function() {
return this.tabs_width;
},
freeze: function() {
$('windows').setStyle({
width: document.viewport.getWidth()+"px",
right: "auto",
});
},
thaw: function() {
$('windows').setStyle({
width: "auto",
right: "0px",
});
},
shiftTabs: function(shift) {
var current = this.tabShift();
var left = current + shift;
var time = Math.min(Math.max(0.1, Math.abs(shift) / 100), 0.5);
this.tabs.style.webkitTransitionDuration = time+"s";
this.tabs.setStyle({left: left+"px"});
this.tabs_layout = this.tabs.getLayout();
},
shiftEnd: function(e) {
this.tabs_layout = this.tabs.getLayout();
this.updateOverflowMenus();
},
makeSortable: function() {
Sortable.create('tabs', {
overlap: 'horizontal',
constraint: 'horizontal',
format: /(.+)/,
only: ["info_tab", "channel_tab", "privmsg_tab"],
onUpdate: function (res) {
var tabs = res.childElements();
var order = tabs.collect(function(t){
var m = t.id.match(/([^_]+)_tab/);
if (m) return m[1]
});
if (order.length) this.connection.sendTabOrder(order);
setTimeout(function(){
this.windows().invoke("updateTabLayout");
this.activeWindow().shiftTab();
}.bind(this), 100);
}.bind(this)
});
},
addMissed: function() {
if (!window.fluid) return;
window.fluid.dockBadge ? window.fluid.dockBadge++ :
window.fluid.dockBadge = 1;
},
clearMissed: function() {
if (!window.fluid) return;
window.fluid.dockBadge = "";
},
ready: function() {
this.freeze();
setTimeout(this.updateOverflowMenus.bind(this), 1000);
this.fetchOembeds(function() {
this.connection.connect(function() {
this.focusHash() || this.activeWindow().focus();
}.bind(this));
}.bind(this));
},
log: function () {
var win = this.activeWindow();
for (var i=0; i < arguments.length; i++) {
if (window.console && window.console.log) {
console.log(arguments[i]);
}
if (this.options.debug == "true") {
if (win) {
win.addMessage({
html: '<li class="message monospace"><div class="left">console</div><div class="msg">'+arguments[i].toString()+'</div></li>'
});
}
}
}
},
msgid: function() {
var ids = this.windows().map(function(w){return w.msgid});
return Math.max.apply(Math, ids);
},
setSource: function(id) {
$('source').value = id;
},
showSet: function(name) {
var ids = this.tabsets[name];
if (ids) {
var elem = $('tabset_menu').select('li').find(function(li) {
return li.innerHTML.unescapeHTML() == name;
});
elem.up('ul').select('li').invoke('removeClassName', 'selectedset');
elem.addClassName('selectedset');
this.windows().each(function(win) {
ids.indexOf(win.id) >= 0 || win.type == "privmsg" ? win.show() : win.hide();
});
this.selectSet(name);
var active = this.activeWindow();
if (!active.visible) {
active = this.nextWindow();
}
if (active) active.shiftTab();
setTimeout(this.updateOverflowMenus.bind(this), 2000);
}
},
selectSet: function(name) {
var hash = window.location.hash;
hash = hash.replace(/^[^\/]*/, name);
window.location.hash = hash;
window.location = window.location.toString();
this.selectedSet = name;
},
clearSet: function(elem) {
elem.up('ul').select('li').invoke('removeClassName', 'selectedset');
elem.addClassName('selectedset');
this.windows().invoke("show");
this.selectSet('');
this.updateOverflowMenus();
this.activeWindow().shiftTab();
},
currentSetContains: function(win) {
var set = this.selectedSet;
if (win.type == "channel" && set && this.tabsets[set]) {
return (this.tabsets[set].indexOf(win.id) >= 0);
}
return true;
},
displayTopic: function(new_topic) {
this.topic.update(new_topic || "no topic set");
Alice.makeLinksClickable(this.topic);
},
displayNicks: function(nicks) {
this.nicklist.innerHTML = nicks.sort(function(a, b) {
a = a.toLowerCase();
b = b.toLowerCase();
if (a > b)
return 1
if (a < b)
return -1
return 0;
}).map(function(nick) {
return '<li><a>'+nick.escapeHTML()+'</a></li>';
}).join("");
},
toggleNicklist: function() {
var windows = $('windows');
var win = this.activeWindow();
var position = win.captureScrollPosition();
if (windows.hasClassName('nicklist'))
windows.removeClassName('nicklist');
else
windows.addClassName('nicklist');
win.scrollToPosition(position);
},
setupNicklist: function() {
this.nicklist.observe("click", function(e) {
share/static/alice.js view on Meta::CPAN
this.addMissed();
}
}
];
},
toggleOverlay: function () {
this.overlayVisible = !this.overlayVisible;
var opacity = this.overlayVisible ? 1 : 0;
$$("li.avatar span.nick").each(function(span){
span.style.opacity = opacity;
});
}
});
Alice.Connection = {
gotoLogin: function() {
window.location = "/login";
},
connect: function(cb) {
if (this.reconnect_count > 3) {
this.aborting = true;
this.changeStatus("ok");
if (this.type == "websocket") {
this.application.activeWindow().showAlert("WebSocket connection failed, falling back...");
this.application.connection = new Alice.Connection.XHR(this.application);
this.application.connection.connect();
return;
}
this.application.activeWindow().showAlert("Alice server is not responding (<a href='javascript:alice.connection.reconnect()'>reconnect</a>)");
return;
}
this.pings = [];
this.closeConnection();
this.len = 0;
this.reconnect_count++;
this.changeStatus("loading");
this._connect(cb);
},
changeStatus: function(classname) {
$('connection_status').className = classname;
},
reconnect: function () {
this.reconnecting = true;
this.reconnect_count = 0;
this.connect();
},
handleException: function(request, exception) {
this.application.log("encountered an error with stream.");
this.application.log(exception);
this.connected = false;
if (!this.aborting)
setTimeout(this.connect.bind(this), 2000);
else
this.changeStatus("ok");
},
handleComplete: function(transport) {
this.application.log("connection was closed cleanly.");
this.connected = false;
if (!this.aborting)
setTimeout(this.connect.bind(this), 2000);
else
this.changeStatus("ok");
},
processQueue: function(data) {
try {
var queue = data.queue;
var length = queue.length;
for (var i=0; i<length; i++) {
if (queue[i].type == "identify") {
this.id = queue[i].id;
}
else if (queue[i].type == "action")
this.application.handleAction(queue[i]);
else if (queue[i].type == "message") {
if (queue[i].timestamp)
queue[i].timestamp = Alice.epochToLocal(queue[i].timestamp, this.application.options.timeformat);
this.application.displayMessage(queue[i]);
}
else if (queue[i].type == "chunk") {
this.application.displayChunk(queue[i]);
}
}
}
catch (e) {
this.application.log(e.toString());
}
},
get: function(path, callback) {
new Ajax.Request(path, {
method: 'get',
on401: this.gotoLogin,
onSuccess: callback
});
},
sendTabOrder: function (windows) {
new Ajax.Request('/tabs', {
method: 'post',
on401: this.gotoLogin,
parameters: {tabs: windows}
});
},
requestChunk: function (win, limit, max) {
if (!this.connected) return;
this.sendMessage({
source: win,
msg: "/chunk " + limit + " " + max,
});
}
};
Alice.Connection.WebSocket = Class.create(Alice.Connection, {
initialize: function(application) {
this.type = "websocket";
this.application = application;
this.connected = false;
this.aborting = false;
this.request = null;
this.reconnect_count = 0;
this.reconnecting = false;
},
_connect: function(cb) {
var now = new Date();
this.application.log("opening new websocket stream");
this.changeStatus("ok");
var parameters = Object.toQueryString({
t: now.getTime() / 1000,
tab: this.application.activeWindow().id
});
var protocol = (window.location.protocol.match(/^https/) ? "wss://" : "ws://");
var url = protocol + window.location.host + "/wsstream?" + parameters;
this.request = new WebSocket(url);
this.request.onopen = function(){
this.connected = true;
setTimeout(cb, 100);
}.bind(this);
this.request.onmessage = this.handleUpdate.bind(this);
this.request.onerror = this.handleException.bind(this);
this.request.onclose = this.handleComplete.bind(this);
},
handleUpdate: function(e) {
var data = e.data.evalJSON();
this.processQueue(data);
},
sendMessage: function(form) {
if (!this.connected) return false;
var params = form;
if (form.nodeName && form.nodeName == "FORM") {
params = Form.serialize(form, true);
}
params['stream'] = this.id;
this.request.send(Object.toJSON(params));
return true;
},
closeConnection: function() {
this.aborting = true;
if (this.request) this.request.close();
this.aborting = false;
},
closeWindow: function(win) {
this.sendMessage({source: win.id, msg: "/close"});
},
handleException: function(exception) {
this.application.log("encountered an error with stream.");
this.application.log(exception);
this.connected = false;
if (!this.aborting)
setTimeout(this.connect.bind(this), 2000);
else
this.changeStatus("ok");
},
requestWindow: function(title, windowId, message) {
this.sendMessage({source: windowId, msg: "/create " + title});
if (message) {
setTimeout(function() {
this.application.displayMessage(message)
}.bind(this), 1000);
}
}
});
Alice.Connection.XHR = Class.create(Alice.Connection, {
initialize: function(application) {
this.type = "xhr";
this.pings = [];
this.pingLimit = 10;
this.seperator = "--xalicex\n";
this.len = 0;
this.application = application;
this.connected = false;
this.aborting = false;
this.request = null;
this.reconnect_count = 0;
this.reconnecting = false;
},
_connect: function(cb) {
setTimeout(function () {
var now = new Date();
this.application.log("opening new xhr stream");
this.changeStatus("ok");
this.request = new Ajax.Request('/stream', {
method: 'get',
parameters: {
t: now.getTime() / 1000,
tab: this.application.activeWindow().id
},
on401: this.gotoLogin,
on500: this.gotoLogin,
on502: this.gotoLogin,
on503: this.gotoLogin,
onException: this.handleException.bind(this),
onInteractive: function(transport) {
if (!this.connected) {
this.connected = true;
setTimeout(cb, 0);
}
this.handleUpdate(transport);
}.bind(this),
onComplete: this.handleComplete.bind(this)
});
}.bind(this), this.application.loadDelay);
},
handleUpdate: function(transport) {
if (this.reconnecting) {
this.application.activeWindow().showHappyAlert("Reconnected to the Alice server");
this.reconnecting = false;
}
this.reconnect_count = 0;
var time = new Date();
var data = transport.responseText.slice(this.len);
var start, end;
start = data.indexOf(this.seperator);
if (start > -1) {
start += this.seperator.length;
end = data.indexOf(this.seperator, start);
if (end == -1) return;
}
else return;
this.len += (end + this.seperator.length) - start;
data = data.slice(start, end);
var data = data.evalJSON();
this.processQueue(data);
if (data.time) {
var lag = this.addPing(time / 1000 - data.time);
if (lag > 5) {
this.application.log("lag is over 5s, reconnecting.");
this.connect();
}
}
},
addPing: function(ping) {
this.pings.push(ping);
if (this.pings.length > this.pingLimit)
this.pings.shift();
var lag = this.lag();
return lag;
},
lag: function() {
if (!this.pings.length) return 0;
return this.pings.inject(0, function (acc, n) {return acc + n}) / this.pings.length;
},
sendMessage: function(form) {
if (!this.connected) return false;
var params;
if (form.nodeName && form.nodeName == "FORM") {
params = Form.serialize(form);
}
else {
params = form;
}
params['stream'] = this.id;
new Ajax.Request('/say', {
method: 'post',
parameters: params,
on401: this.gotoLogin,
onException: function (request, exception) {
alert("There was an error sending a message.");
}
});
return true;
},
closeConnection: function() {
this.aborting = true;
if (this.request && this.request.transport)
this.request.transport.abort();
this.aborting = false;
},
closeWindow: function(win) {
new Ajax.Request('/say', {
method: 'post',
on401: this.gotoLogin,
parameters: {
source: win.id,
msg: "/close",
stream: this.id
}
});
},
requestWindow: function(title, windowId, message) {
new Ajax.Request('/say', {
method: 'post',
parameters: {
source: windowId,
msg: "/create " + title,
stream: this.id
},
on401: this.gotoLogin,
onSuccess: function (transport) {
this.handleUpdate(transport);
if (message) {
setTimeout(function() {
this.application.displayMessage(message)
}.bind(this), 1000);
}
}.bind(this)
});
}
});
Alice.Window = Class.create({
initialize: function(application, serialized, msgid) {
this.application = application;
this.element = $(serialized['id']);
this.title = serialized['title'];
this.type = serialized['type'];
this.hashtag = serialized['hashtag'];
this.id = this.element.identify();
this.active = false;
this.topic = serialized['topic'];
this.tab = $(this.id + "_tab");
this.tab_layout = this.tab.getLayout();
this.tabButton = $(this.id + "_tab_button");
this.messages = this.element.down('.messages');
this.visibleNick = "";
this.visibleNickTimeout = "";
this.lasttimestamp = new Date(0);
this.nicks = [];
this.nicks_order = [];
this.statuses = [];
this.messageLimit = this.application.isMobile ? 50 : 100;
this.chunkSize = this.messageLimit / 2;
this.msgid = msgid || 0;
this.visible = true;
this.forceScroll = false;
this.lastScrollPosition = 0;
this.setupEvents();
},
hide: function() {
this.element.hide();
this.tab.addClassName('hidden');
this.tab.removeClassName('visible');
this.visible = false;
},
show: function() {
this.element.show();
this.tab.addClassName('visible');
this.tab.removeClassName('hidden');
this.visible = true;
this.updateTabLayout();
},
setupEvents: function() {
this.application.supportsTouch ? this.setupTouchEvents() : this.setupMouseEvents();
},
setupTouchEvents: function() {
this.messages.observe("touchstart", function (e) {
this.showNick(e);
}.bind(this));
this.tab.observe("touchstart", function (e) {
e.stop();
if (!this.active) this.focus();
}.bind(this));
this.tabButton.observe("touchstart", function(e) {
if (this.active) {
e.stop();
confirm("Are you sure you want to close this tab?") && this.close()
}
}.bind(this));
},
setupMouseEvents: function() {
this.tab.observe("mousedown", function(e) {
if (!this.active) {this.focus(); this.focusing = true}
}.bind(this));
this.tab.observe("click", function(e) {this.focusing = false}.bind(this));
this.tabButton.observe("click", function(e) {
if (this.active && !this.focusing)
if (this.type != "channel" || confirm("Are you sure you want to leave "+this.title+"?"))
this.close()
}.bind(this));
this.messages.observe("mouseover", this.showNick.bind(this));
},
checkScrollBack: function() {
if (this.active && this.element.scrollTop == 0) {
clearInterval(this.scrollListener);
var first = this.messages.down("li[id]");
if (first) {
first = first.id.replace("msg-", "") - 1;
this.messageLimit += this.chunkSize;
}
else {
first = this.msgid;
}
this.application.log("requesting chunk" + first);
this.tab.addClassName("loading");
this.application.getBacklog(this, first, this.chunkSize);
}
else {
clearTimeout(this.scrollListener);
this.scrollListener = setTimeout(this.checkScrollBack.bind(this), 1000);
}
},
clearMessages: function() {
clearTimeout(this.scrollListener);
this.messages.update("");
this.lastNick = "";
},
updateTabLayout: function() {
this.tab_layout = this.tab.getLayout();
},
getTabPosition: function() {
var shift = this.application.tabShift();
var tabs_width = this.application.tabsWidth();
var tab_width = this.tab_layout.get("width");
var offset_left = this.tab_layout.get("left") + shift;
var offset_right = tabs_width - (offset_left + tab_width);
var overflow_right = Math.abs(Math.min(0, offset_right));
var overflow_left = Math.abs(Math.min(0, offset_left));
return {
right: overflow_right,
left: overflow_left
};
},
shiftTab: function() {
var left = null
, time = 0
, pos = this.getTabPosition();
if (pos.left) {
this.application.shiftTabs(pos.left);
}
else if (pos.right) {
this.application.shiftTabs(-pos.right);
}
},
unFocus: function() {
this.lastScrollPosition = this.captureScrollPosition();
this.active = false;
this.element.removeClassName('active');
this.tab.removeClassName('active');
clearTimeout(this.scrollListener);
this.addFold();
},
addFold: function() {
this.messages.select("li.fold").invoke("removeClassName", "fold");
var last = this.messages.childElements().last();
if (last) last.addClassName("fold");
},
showNick: function (e) {
var li = e.findElement("li.message");
if (li && li.hasClassName("avatar")) {
if (this.application.overlayVisible || li == this.visibleNick) return;
clearTimeout(this.visibleNickTimeout);
this.visibleNick = li;
var nick;
if (li.hasClassName("consecutive")) {
var stem = li.previous("li:not(.consecutive)");
if (!stem) return;
nick = stem.down("span.nick");
} else {
nick = li.down("span.nick");
}
if (nick) {
this.visibleNickTimeout = setTimeout(function(nick) {
if (nick) nick.style.opacity = 1;
setTimeout(function(){
if (this.application.overlayVisible) return;
if (nick) nick.style.opacity = 0;
}.bind(this, nick) , 1000);
}.bind(this, nick), 500);
}
}
else {
this.visibleNick = "";
clearTimeout(this.visibleNickTimeout);
}
},
focus: function(event) {
if (!this.application.currentSetContains(this)) return;
this.application.previousFocus = this.application.activeWindow();
if (this != this.application.previousFocus)
this.application.previousFocus.unFocus();
this.element.addClassName('active');
this.tab.addClassName('active');
if (!this.active) {
this.active = true;
setTimeout(function(){
this.scrollToPosition(this.lastScrollPosition);
if (!this.scrollBackEmpty) this.checkScrollBack();
}.bind(this), 0);
}
this.application.setSource(this.id);
this.application.displayNicks(this.nicks);
this.markRead();
this.setWindowHash();
this.shiftTab();
var last = this.messages.childElements().last();
if (last && last.hasClassName("fold"))
last.removeClassName("fold");
this.application.displayTopic(this.topic);
document.title = this.title;
return this;
},
setWindowHash: function () {
var new_hash = "#" + this.application.selectedSet + this.hashtag;
if (new_hash != window.location.hash) {
window.location.hash = encodeURI(new_hash);
window.location = window.location.toString();
}
},
markRead: function () {
this.tab.removeClassName("unread");
this.tab.removeClassName("highlight");
this.statuses = [];
this.application.unHighlightChannelSelect(this.id);
},
markUnread: function(classname) {
var classes = ["unread"];
if (classname) classes.push(classname);
classes.each(function(c){this.tab.addClassName(c)}.bind(this));
this.application.highlightChannelSelect(this.id, classes);
this.statuses = classes;
},
disable: function () {
this.markRead();
this.tab.addClassName('disabled');
},
enable: function () {
this.tab.removeClassName('disabled');
},
close: function(event) {
this.tab.remove();
this.element.remove();
this.application.removeWindow(this);
},
showHappyAlert: function (message) {
this.messages.insert(
"<li class='event happynotice'><div class='msg'>"+message+"</div></li>"
);
this.scrollToPosition(0);
},
showAlert: function (message) {
this.messages.insert(
"<li class='event notice'><div class='msg'>"+message+"</div></li>"
);
this.scrollToPosition(0);
},
announce: function (message) {
message = message.escapeHTML();
this.messages.insert(
"<li class='message monospaced announce'><div class='msg'>"+message+"</div></li>"
);
this.scrollToPosition(0);
},
trimMessages: function() {
this.messages.select("li").reverse().slice(this.messageLimit).invoke("remove");
},
addChunk: function(chunk) {
if (chunk.nicks) this.updateNicks(chunk.nicks);
clearTimeout(this.scrollListener);
if (chunk.range.length == 0) {
this.scrollBackEmpty = true;
this.tab.removeClassName("loading");
return;
}
var position = this.captureScrollPosition();
if (chunk['range'][0] > this.msgid) {
this.messages.insert({"bottom": chunk['html']});
this.trimMessages();
this.msgid = chunk['range'][1];
}
else {
this.bulk_insert = true;
this.messages.insert({"top": chunk['html']});
}
this.messages.select("li:not(.filtered)").each(function (li) {
this.application.applyFilters(li, this);
}.bind(this));
this.bulk_insert = false;
this.scrollToPosition(position);
setTimeout(function(){this.removeClassName("loading")}.bind(this.tab), 1000);
this.scrollListener = setTimeout(this.checkScrollBack.bind(this), 1000);
},
addMessage: function(message) {
if (!message.html || message.msgid <= this.msgid) return;
if (message.msgid) this.msgid = message.msgid;
if (message.nicks) this.updateNicks(message.nicks);
var position = this.captureScrollPosition();
this.messages.insert(message.html);
this.trimMessages();
this.scrollToPosition(position);
var li = this.messages.select("li").last();
this.application.applyFilters(li, this);
this.scrollToPosition(position);
if (message.event == "topic") {
this.topic = message.body;
if (this.active) this.application.displayTopic(this.topic);
}
this.element.redraw();
this.promoteNick(message.nick);
},
promoteNick: function(nick) {
if (this.nicks_order.last() == nick) return;
var index = this.nicks_order.indexOf(nick);
if (index > -1) this.nicks_order.splice(index, 1);
this.nicks_order.push(nick);
},
captureScrollPosition: function() {
if (!this.active) return;
if (this.forceScroll) return 0;
var bottom = this.element.scrollTop + this.element.offsetHeight;
var height = this.element.scrollHeight;
return height - bottom;
},
scrollToPosition: function(position) {
if (!this.active) return;
this.element.scrollTop = this.element.scrollHeight - this.element.offsetHeight - position;
},
getNicknames: function () {
return this.nicks.sort(function(a, b) {
var pos_a = this.nicks_order.indexOf(a),
pos_b = this.nicks_order.indexOf(b);
if (pos_a == pos_b)
share/static/alice.js view on Meta::CPAN
if (window == window.parent) {
document.observe("dom:loaded", function () {
var alice = new Alice.Application();
window.alice = alice;
$('helpclose').observe("click", function () { $('help').hide(); });
$('nicklist_toggle').observe("click", function () { alice.toggleNicklist() });
$$('.dropdown').each(function (menu) {
menu.observe(alice.supportsTouch ? "touchstart" : "mousedown", function (e) {
e.stop();
var element = e.element('.dropdown');
if (element.hasClassName("dropdown")) {
if (menu.hasClassName("open")) {
menu.removeClassName("open");
}
else {
$$(".dropdown.open").invoke("removeClassName", "open");
menu.addClassName("open");
}
}
});
});
document.observe(alice.supportsTouch ? "touchend" : "mouseup", function (e) {
if (e.findElement('.dropdown')) return;
$$('.dropdown.open').invoke("removeClassName", "open");
});
if (alice.isMobile) {
$('nicklist_toggle').addClassName('visible');
}
else {
window.onkeydown = function (e) {
if (!alice.input.disabled && !Alice.isSpecialKey(e.which))
alice.input.focus();
};
var windows = $('windows');
var toggle = $('nicklist_toggle');
var resize = function () {
var active = alice.activeWindow();
var position = active.captureScrollPosition();
var end = function(){
alice.freeze();
alice.tabs_width = $('tabs_container').getWidth();
alice.updateOverflowMenus();
active.scrollToPosition(position);
active.shiftTab();
window.onresize = resize;
};
var end_timer;
window.onresize = function() {
clearTimeout(end_timer);
end_timer = setTimeout(end, 1000);
};
};
window.onresize = resize;
var move = function(e) {
var width = document.viewport.getWidth();
var left = windows.hasClassName('nicklist') ? 200 : 100;
var visible = toggle.hasClassName('visible');
if (!visible && width - e.pointerX() > left)
return;
toggle.addClassName('visible');
var end = function() {
toggle.removeClassName('visible');
window.onmousemove = move;
};
var end_timer;
window.onmousemove = function() {
clearTimeout(end_timer);
end_timer = setTimeout(end, 1000);
};
};
window.onmousemove = move;
window.onfocus = function () {
alice.input.focus();
alice.freeze();
alice.tabs_width = $('tabs_container').getWidth();
alice.updateOverflowMenus();
alice.isFocused = true
alice.clearMissed();
};
window.status = " ";
window.onblur = function () {
alice.isFocused = false
};
}
window.onhashchange = function (e) {alice.focusHash()};
window.onorientationchange = function() {
var active = alice.activeWindow();
active.scrollToPosition(0);
alice.freeze();
active.shiftTab();
};
document.observe("copy", function(e) {
if (!e.findElement("ul.messages")) return;
if(!Prototype.Browser.IE && typeof window.getSelection !== 'undefined') {
var buffer = new Element("DIV", {"class": "copybuffer"});
document.getElementsByTagName("body")[0].appendChild(buffer);
var sel = window.getSelection();
var range = sel.getRangeAt(0);
buffer.appendChild(range.cloneContents());
Alice.cleanupCopy(buffer);
sel.selectAllChildren(buffer);
setTimeout(function() {
if(typeof window.getSelection().setBaseAndExtent !== 'undefined') {
sel.setBaseAndExtent(
range.startContainer,
range.startOffset,
range.endContainer,
range.endOffset
);
}
}, 0);
}
});
if (alice.isMobile) return;
alice.addFilters([
function(msg, win) {
msg.select("a").filter(function(a) {
return Alice.RE.audio.match(a.href);
}).each(function(a) {
var img = new Element("IMG", {"class": "audio", src: "/static/image/play.png"});
img.onclick = function(){ Alice.playAudio(img) };
a.insert({before: img})
});
},
function (msg, win) {
if (alice.options.images == "show") {
var matches = msg.select("a").inject(0, function(acc, a) {
var oembed = alice.oembeds.find(function(service) {
return service.match(a.href);
});
if (oembed) {
alice.embed(a, win);
acc++;
}
return acc;
});
return matches > 0;
}
},
function (msg, win) {
msg.select("a").filter(function(a) {
var img = a.readAttribute("img") || a.innerHTML;
return img.match(Alice.RE.img);
}).each(function(a) {
var image = a.readAttribute("img") || a.href;
if (alice.options.images == "show" && !image.match(/#(nsfw|hide)$/))
win.inlineImage(a);
else
a.observe("click", function(e){e.stop();win.inlineImage(a)});
});
}
]);
if (window.navigator.userAgent.match(/chrome/i)) {
alice.addFilters([
function(msg, win) {
msg.setStyle({borderWidthTop: "1px"});
}
]);
( run in 0.717 second using v1.01-cache-2.11-cpan-39bf76dae61 )