Created
December 18, 2012 04:48
-
-
Save whiteleaf7/4325096 to your computer and use it in GitHub Desktop.
Limechat Quake Notifier
去年の震災直後に作った 、P2P地震情報をLimechatと連携させるソフト。お蔵入りにしてたけれどもったいないので保存
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Simple Ini file controller | |
// | |
// [ ] で囲まれたものはセクションとして扱われ、それ以降のデータはセクションに所属する | |
// セクションが登場するまでのデータは global というセクションに割り当てられる | |
// | |
// var ini = new Ini("setting.ini"); | |
// value1 = ini.data.global.key; | |
// value2 = ini.data.section.key; | |
// | |
var Ini = Class.create({ | |
delimiter: "=", | |
global_section: "global", | |
initialize: function(filename) { | |
this.filename = filename; | |
}, | |
clear: function() { | |
this.data = { global: {} }; | |
}, | |
load: function() { | |
this.clear(); | |
var file = openFile(this.filename); | |
if (file) { | |
var section = this.global_section; | |
var line; | |
while ((line = file.readLine()) != null) { | |
if (line.trim() == 0) continue; | |
if (/^\[(.+?)\]/.test(line)) { | |
section = RegExp.$1.trim(); | |
if (!this.data[section]) { | |
this.data[section] = {}; | |
} | |
continue; | |
} | |
var res = line.split(this.delimiter); | |
if (res.length != 2) continue; | |
var key = res[0].trim(), value = res[1].trim(); | |
this.data[section][key] = value; | |
} | |
file.close(); | |
return true; | |
} | |
return false; | |
}, | |
save: function() { | |
var file = openFile(this.filename, false); | |
if (file) { | |
for (var section in this.data) { | |
if (section != this.global_section) { | |
file.writeLine("[" + section + "]"); | |
} | |
for (var key in this.data[section]) { | |
file.writeLine(key + this.delimiter + this.data[section][key]); | |
} | |
} | |
file.truncate(); | |
file.close(); | |
return true; | |
} | |
return false; | |
} | |
}); | |
String.prototype.trim = function() { | |
return this.replace(/(^\s+)|(\s+$)/g, ""); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Prototype JavaScript framework, version 1.7 | |
* (c) 2005-2010 Sam Stephenson | |
* | |
* Prototype is freely distributable under the terms of an MIT-style license. | |
* For details, see the Prototype web site: http://www.prototypejs.org/ | |
* | |
*--------------------------------------------------------------------------*/ | |
var document={getElementsByTagName:function(){return[{src:''}];}, | |
createElement:function(){return{appendChild:function(){}};}, | |
createTextNode:function(){},createEvent:function(){return{__proto__:{}};}, | |
getElementById:function(){return{};},write:function(){},domain:'localhost'}; | |
var window={document:document,attachEvent:function(){},setTimeout:function(a,b){setTimeout(a,b);}}; | |
var navigator={userAgent:'LimeChat'};var location={protocol:'http:',port:''}; | |
var Prototype = { | |
Version: '1.7', | |
Browser: (function(){ | |
var ua = navigator.userAgent; | |
var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; | |
return { | |
IE: !!window.attachEvent && !isOpera, | |
Opera: isOpera, | |
WebKit: ua.indexOf('AppleWebKit/') > -1, | |
Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, | |
MobileSafari: /Apple.*Mobile/.test(ua) | |
} | |
})(), | |
BrowserFeatures: { | |
XPath: !!document.evaluate, | |
SelectorsAPI: !!document.querySelector, | |
ElementExtensions: (function() { | |
var constructor = window.Element || window.HTMLElement; | |
return !!(constructor && constructor.prototype); | |
})(), | |
SpecificElementExtensions: (function() { | |
if (typeof window.HTMLDivElement !== 'undefined') | |
return true; | |
var div = document.createElement('div'), | |
form = document.createElement('form'), | |
isSupported = false; | |
if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { | |
isSupported = true; | |
} | |
div = form = null; | |
return isSupported; | |
})() | |
}, | |
ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', | |
JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, | |
emptyFunction: function() { }, | |
K: function(x) { return x } | |
}; | |
if (Prototype.Browser.MobileSafari) | |
Prototype.BrowserFeatures.SpecificElementExtensions = false; | |
var Abstract = { }; | |
var Try = { | |
these: function() { | |
var returnValue; | |
for (var i = 0, length = arguments.length; i < length; i++) { | |
var lambda = arguments[i]; | |
try { | |
returnValue = lambda(); | |
break; | |
} catch (e) { } | |
} | |
return returnValue; | |
} | |
}; | |
/* Based on Alex Arnell's inheritance implementation. */ | |
var Class = (function() { | |
var IS_DONTENUM_BUGGY = (function(){ | |
for (var p in { toString: 1 }) { | |
if (p === 'toString') return false; | |
} | |
return true; | |
})(); | |
function subclass() {}; | |
function create() { | |
var parent = null, properties = $A(arguments); | |
if (Object.isFunction(properties[0])) | |
parent = properties.shift(); | |
function klass() { | |
this.initialize.apply(this, arguments); | |
} | |
Object.extend(klass, Class.Methods); | |
klass.superclass = parent; | |
klass.subclasses = []; | |
if (parent) { | |
subclass.prototype = parent.prototype; | |
klass.prototype = new subclass; | |
parent.subclasses.push(klass); | |
} | |
for (var i = 0, length = properties.length; i < length; i++) | |
klass.addMethods(properties[i]); | |
if (!klass.prototype.initialize) | |
klass.prototype.initialize = Prototype.emptyFunction; | |
klass.prototype.constructor = klass; | |
return klass; | |
} | |
function addMethods(source) { | |
var ancestor = this.superclass && this.superclass.prototype, | |
properties = Object.keys(source); | |
if (IS_DONTENUM_BUGGY) { | |
if (source.toString != Object.prototype.toString) | |
properties.push("toString"); | |
if (source.valueOf != Object.prototype.valueOf) | |
properties.push("valueOf"); | |
} | |
for (var i = 0, length = properties.length; i < length; i++) { | |
var property = properties[i], value = source[property]; | |
if (ancestor && Object.isFunction(value) && | |
value.argumentNames()[0] == "$super") { | |
var method = value; | |
value = (function(m) { | |
return function() { return ancestor[m].apply(this, arguments); }; | |
})(property).wrap(method); | |
value.valueOf = method.valueOf.bind(method); | |
value.toString = method.toString.bind(method); | |
} | |
this.prototype[property] = value; | |
} | |
return this; | |
} | |
return { | |
create: create, | |
Methods: { | |
addMethods: addMethods | |
} | |
}; | |
})(); | |
(function() { | |
var _toString = Object.prototype.toString, | |
NULL_TYPE = 'Null', | |
UNDEFINED_TYPE = 'Undefined', | |
BOOLEAN_TYPE = 'Boolean', | |
NUMBER_TYPE = 'Number', | |
STRING_TYPE = 'String', | |
OBJECT_TYPE = 'Object', | |
FUNCTION_CLASS = '[object Function]', | |
BOOLEAN_CLASS = '[object Boolean]', | |
NUMBER_CLASS = '[object Number]', | |
STRING_CLASS = '[object String]', | |
ARRAY_CLASS = '[object Array]', | |
DATE_CLASS = '[object Date]', | |
NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON && | |
typeof JSON.stringify === 'function' && | |
JSON.stringify(0) === '0' && | |
typeof JSON.stringify(Prototype.K) === 'undefined'; | |
function Type(o) { | |
switch(o) { | |
case null: return NULL_TYPE; | |
case (void 0): return UNDEFINED_TYPE; | |
} | |
var type = typeof o; | |
switch(type) { | |
case 'boolean': return BOOLEAN_TYPE; | |
case 'number': return NUMBER_TYPE; | |
case 'string': return STRING_TYPE; | |
} | |
return OBJECT_TYPE; | |
} | |
function extend(destination, source) { | |
for (var property in source) | |
destination[property] = source[property]; | |
return destination; | |
} | |
function inspect(object) { | |
try { | |
if (isUndefined(object)) return 'undefined'; | |
if (object === null) return 'null'; | |
return object.inspect ? object.inspect() : String(object); | |
} catch (e) { | |
if (e instanceof RangeError) return '...'; | |
throw e; | |
} | |
} | |
function toJSON(value) { | |
return Str('', { '': value }, []); | |
} | |
function Str(key, holder, stack) { | |
var value = holder[key], | |
type = typeof value; | |
if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { | |
value = value.toJSON(key); | |
} | |
var _class = _toString.call(value); | |
switch (_class) { | |
case NUMBER_CLASS: | |
case BOOLEAN_CLASS: | |
case STRING_CLASS: | |
value = value.valueOf(); | |
} | |
switch (value) { | |
case null: return 'null'; | |
case true: return 'true'; | |
case false: return 'false'; | |
} | |
type = typeof value; | |
switch (type) { | |
case 'string': | |
return value.inspect(true); | |
case 'number': | |
return isFinite(value) ? String(value) : 'null'; | |
case 'object': | |
for (var i = 0, length = stack.length; i < length; i++) { | |
if (stack[i] === value) { throw new TypeError(); } | |
} | |
stack.push(value); | |
var partial = []; | |
if (_class === ARRAY_CLASS) { | |
for (var i = 0, length = value.length; i < length; i++) { | |
var str = Str(i, value, stack); | |
partial.push(typeof str === 'undefined' ? 'null' : str); | |
} | |
partial = '[' + partial.join(',') + ']'; | |
} else { | |
var keys = Object.keys(value); | |
for (var i = 0, length = keys.length; i < length; i++) { | |
var key = keys[i], str = Str(key, value, stack); | |
if (typeof str !== "undefined") { | |
partial.push(key.inspect(true)+ ':' + str); | |
} | |
} | |
partial = '{' + partial.join(',') + '}'; | |
} | |
stack.pop(); | |
return partial; | |
} | |
} | |
function stringify(object) { | |
return JSON.stringify(object); | |
} | |
function toQueryString(object) { | |
return $H(object).toQueryString(); | |
} | |
function toHTML(object) { | |
return object && object.toHTML ? object.toHTML() : String.interpret(object); | |
} | |
function keys(object) { | |
if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } | |
var results = []; | |
for (var property in object) { | |
if (object.hasOwnProperty(property)) { | |
results.push(property); | |
} | |
} | |
return results; | |
} | |
function values(object) { | |
var results = []; | |
for (var property in object) | |
results.push(object[property]); | |
return results; | |
} | |
function clone(object) { | |
return extend({ }, object); | |
} | |
function isElement(object) { | |
return !!(object && object.nodeType == 1); | |
} | |
function isArray(object) { | |
return _toString.call(object) === ARRAY_CLASS; | |
} | |
var hasNativeIsArray = (typeof Array.isArray == 'function') | |
&& Array.isArray([]) && !Array.isArray({}); | |
if (hasNativeIsArray) { | |
isArray = Array.isArray; | |
} | |
function isHash(object) { | |
return object instanceof Hash; | |
} | |
function isFunction(object) { | |
return _toString.call(object) === FUNCTION_CLASS; | |
} | |
function isString(object) { | |
return _toString.call(object) === STRING_CLASS; | |
} | |
function isNumber(object) { | |
return _toString.call(object) === NUMBER_CLASS; | |
} | |
function isDate(object) { | |
return _toString.call(object) === DATE_CLASS; | |
} | |
function isUndefined(object) { | |
return typeof object === "undefined"; | |
} | |
extend(Object, { | |
extend: extend, | |
inspect: inspect, | |
toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON, | |
toQueryString: toQueryString, | |
toHTML: toHTML, | |
keys: Object.keys || keys, | |
values: values, | |
clone: clone, | |
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; | |
})(Date.prototype); | |
RegExp.prototype.match = RegExp.prototype.test; | |
RegExp.escape = function(str) { | |
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); | |
}; | |
var PeriodicalExecuter = Class.create({ | |
initialize: function(callback, frequency) { | |
this.callback = callback; | |
this.frequency = frequency; | |
this.currentlyExecuting = false; | |
this.registerCallback(); | |
}, | |
registerCallback: function() { | |
this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); | |
}, | |
execute: function() { | |
this.callback(this); | |
}, | |
stop: function() { | |
if (!this.timer) return; | |
clearInterval(this.timer); | |
this.timer = null; | |
}, | |
onTimerEvent: function() { | |
if (!this.currentlyExecuting) { | |
try { | |
this.currentlyExecuting = true; | |
this.execute(); | |
this.currentlyExecuting = false; | |
} catch(e) { | |
this.currentlyExecuting = false; | |
throw e; | |
} | |
} | |
} | |
}); | |
Object.extend(String, { | |
interpret: function(value) { | |
return value == null ? '' : String(value); | |
}, | |
specialChar: { | |
'\b': '\\b', | |
'\t': '\\t', | |
'\n': '\\n', | |
'\f': '\\f', | |
'\r': '\\r', | |
'\\': '\\\\' | |
} | |
}); | |
Object.extend(String.prototype, (function() { | |
var NATIVE_JSON_PARSE_SUPPORT = window.JSON && | |
typeof JSON.parse === 'function' && | |
JSON.parse('{"test": true}').test; | |
function prepareReplacement(replacement) { | |
if (Object.isFunction(replacement)) return replacement; | |
var template = new Template(replacement); | |
return function(match) { return template.evaluate(match) }; | |
} | |
function gsub(pattern, replacement) { | |
var result = '', source = this, match; | |
replacement = prepareReplacement(replacement); | |
if (Object.isString(pattern)) | |
pattern = RegExp.escape(pattern); | |
if (!(pattern.length || pattern.source)) { | |
replacement = replacement(''); | |
return replacement + source.split('').join(replacement) + replacement; | |
} | |
while (source.length > 0) { | |
if (match = source.match(pattern)) { | |
result += source.slice(0, match.index); | |
result += String.interpret(replacement(match)); | |
source = source.slice(match.index + match[0].length); | |
} else { | |
result += source, source = ''; | |
} | |
} | |
return result; | |
} | |
function sub(pattern, replacement, count) { | |
replacement = prepareReplacement(replacement); | |
count = Object.isUndefined(count) ? 1 : count; | |
return this.gsub(pattern, function(match) { | |
if (--count < 0) return match[0]; | |
return replacement(match); | |
}); | |
} | |
function scan(pattern, iterator) { | |
this.gsub(pattern, iterator); | |
return String(this); | |
} | |
function truncate(length, truncation) { | |
length = length || 30; | |
truncation = Object.isUndefined(truncation) ? '...' : truncation; | |
return this.length > length ? | |
this.slice(0, length - truncation.length) + truncation : String(this); | |
} | |
function strip() { | |
return this.replace(/^\s+/, '').replace(/\s+$/, ''); | |
} | |
function stripTags() { | |
return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, ''); | |
} | |
function stripScripts() { | |
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); | |
} | |
function extractScripts() { | |
var matchAll = new RegExp(Prototype.ScriptFragment, 'img'), | |
matchOne = new RegExp(Prototype.ScriptFragment, 'im'); | |
return (this.match(matchAll) || []).map(function(scriptTag) { | |
return (scriptTag.match(matchOne) || ['', ''])[1]; | |
}); | |
} | |
function evalScripts() { | |
return this.extractScripts().map(function(script) { return eval(script) }); | |
} | |
function escapeHTML() { | |
return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); | |
} | |
function unescapeHTML() { | |
return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); | |
} | |
function toQueryParams(separator) { | |
var match = this.strip().match(/([^?#]*)(#.*)?$/); | |
if (!match) return { }; | |
return match[1].split(separator || '&').inject({ }, function(hash, pair) { | |
if ((pair = pair.split('='))[0]) { | |
var key = decodeURIComponent(pair.shift()), | |
value = pair.length > 1 ? pair.join('=') : pair[0]; | |
if (value != undefined) value = decodeURIComponent(value); | |
if (key in hash) { | |
if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; | |
hash[key].push(value); | |
} | |
else hash[key] = value; | |
} | |
return hash; | |
}); | |
} | |
function toArray() { | |
return this.split(''); | |
} | |
function succ() { | |
return this.slice(0, this.length - 1) + | |
String.fromCharCode(this.charCodeAt(this.length - 1) + 1); | |
} | |
function times(count) { | |
return count < 1 ? '' : new Array(count + 1).join(this); | |
} | |
function camelize() { | |
return this.replace(/-+(.)?/g, function(match, chr) { | |
return chr ? chr.toUpperCase() : ''; | |
}); | |
} | |
function capitalize() { | |
return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); | |
} | |
function underscore() { | |
return this.replace(/::/g, '/') | |
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') | |
.replace(/([a-z\d])([A-Z])/g, '$1_$2') | |
.replace(/-/g, '_') | |
.toLowerCase(); | |
} | |
function dasherize() { | |
return this.replace(/_/g, '-'); | |
} | |
function inspect(useDoubleQuotes) { | |
var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { | |
if (character in String.specialChar) { | |
return String.specialChar[character]; | |
} | |
return '\\u00' + character.charCodeAt().toPaddedString(2, 16); | |
}); | |
if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; | |
return "'" + escapedString.replace(/'/g, '\\\'') + "'"; | |
} | |
function unfilterJSON(filter) { | |
return this.replace(filter || Prototype.JSONFilter, '$1'); | |
} | |
function isJSON() { | |
var str = this; | |
if (str.blank()) return false; | |
str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'); | |
str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'); | |
str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, ''); | |
return (/^[\],:{}\s]*$/).test(str); | |
} | |
function evalJSON(sanitize) { | |
var json = this.unfilterJSON(), | |
cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; | |
if (cx.test(json)) { | |
json = json.replace(cx, function (a) { | |
return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); | |
}); | |
} | |
try { | |
if (!sanitize || json.isJSON()) return eval('(' + json + ')'); | |
} catch (e) { } | |
throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); | |
} | |
function parseJSON() { | |
var json = this.unfilterJSON(); | |
return JSON.parse(json); | |
} | |
function include(pattern) { | |
return this.indexOf(pattern) > -1; | |
} | |
function startsWith(pattern) { | |
return this.lastIndexOf(pattern, 0) === 0; | |
} | |
function endsWith(pattern) { | |
var d = this.length - pattern.length; | |
return d >= 0 && this.indexOf(pattern, d) === d; | |
} | |
function empty() { | |
return this == ''; | |
} | |
function blank() { | |
return /^\s*$/.test(this); | |
} | |
function interpolate(object, pattern) { | |
return new Template(this, pattern).evaluate(object); | |
} | |
return { | |
gsub: gsub, | |
sub: sub, | |
scan: scan, | |
truncate: truncate, | |
strip: String.prototype.trim || strip, | |
stripTags: stripTags, | |
stripScripts: stripScripts, | |
extractScripts: extractScripts, | |
evalScripts: evalScripts, | |
escapeHTML: escapeHTML, | |
unescapeHTML: unescapeHTML, | |
toQueryParams: toQueryParams, | |
parseQuery: toQueryParams, | |
toArray: toArray, | |
succ: succ, | |
times: times, | |
camelize: camelize, | |
capitalize: capitalize, | |
underscore: underscore, | |
dasherize: dasherize, | |
inspect: inspect, | |
unfilterJSON: unfilterJSON, | |
isJSON: isJSON, | |
evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON, | |
include: include, | |
startsWith: startsWith, | |
endsWith: endsWith, | |
empty: empty, | |
blank: blank, | |
interpolate: interpolate | |
}; | |
})()); | |
var Template = Class.create({ | |
initialize: function(template, pattern) { | |
this.template = template.toString(); | |
this.pattern = pattern || Template.Pattern; | |
}, | |
evaluate: function(object) { | |
if (object && Object.isFunction(object.toTemplateReplacements)) | |
object = object.toTemplateReplacements(); | |
return this.template.gsub(this.pattern, function(match) { | |
if (object == null) return (match[1] + ''); | |
var before = match[1] || ''; | |
if (before == '\\') return match[2]; | |
var ctx = object, expr = match[3], | |
pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; | |
match = pattern.exec(expr); | |
if (match == null) return before; | |
while (match != null) { | |
var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; | |
ctx = ctx[comp]; | |
if (null == ctx || '' == match[3]) break; | |
expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); | |
match = pattern.exec(expr); | |
} | |
return before + String.interpret(ctx); | |
}); | |
} | |
}); | |
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; | |
var $break = { }; | |
var Enumerable = (function() { | |
function each(iterator, context) { | |
var index = 0; | |
try { | |
this._each(function(value) { | |
iterator.call(context, value, index++); | |
}); | |
} catch (e) { | |
if (e != $break) throw e; | |
} | |
return this; | |
} | |
function eachSlice(number, iterator, context) { | |
var index = -number, slices = [], array = this.toArray(); | |
if (number < 1) return array; | |
while ((index += number) < array.length) | |
slices.push(array.slice(index, index+number)); | |
return slices.collect(iterator, context); | |
} | |
function all(iterator, context) { | |
iterator = iterator || Prototype.K; | |
var result = true; | |
this.each(function(value, index) { | |
result = result && !!iterator.call(context, value, index); | |
if (!result) throw $break; | |
}); | |
return result; | |
} | |
function any(iterator, context) { | |
iterator = iterator || Prototype.K; | |
var result = false; | |
this.each(function(value, index) { | |
if (result = !!iterator.call(context, value, index)) | |
throw $break; | |
}); | |
return result; | |
} | |
function collect(iterator, context) { | |
iterator = iterator || Prototype.K; | |
var results = []; | |
this.each(function(value, index) { | |
results.push(iterator.call(context, value, index)); | |
}); | |
return results; | |
} | |
function detect(iterator, context) { | |
var result; | |
this.each(function(value, index) { | |
if (iterator.call(context, value, index)) { | |
result = value; | |
throw $break; | |
} | |
}); | |
return result; | |
} | |
function findAll(iterator, context) { | |
var results = []; | |
this.each(function(value, index) { | |
if (iterator.call(context, value, index)) | |
results.push(value); | |
}); | |
return results; | |
} | |
function grep(filter, iterator, context) { | |
iterator = iterator || Prototype.K; | |
var results = []; | |
if (Object.isString(filter)) | |
filter = new RegExp(RegExp.escape(filter)); | |
this.each(function(value, index) { | |
if (filter.match(value)) | |
results.push(iterator.call(context, value, index)); | |
}); | |
return results; | |
} | |
function include(object) { | |
if (Object.isFunction(this.indexOf)) | |
if (this.indexOf(object) != -1) return true; | |
var found = false; | |
this.each(function(value) { | |
if (value == object) { | |
found = true; | |
throw $break; | |
} | |
}); | |
return found; | |
} | |
function inGroupsOf(number, fillWith) { | |
fillWith = Object.isUndefined(fillWith) ? null : fillWith; | |
return this.eachSlice(number, function(slice) { | |
while(slice.length < number) slice.push(fillWith); | |
return slice; | |
}); | |
} | |
function inject(memo, iterator, context) { | |
this.each(function(value, index) { | |
memo = iterator.call(context, memo, value, index); | |
}); | |
return memo; | |
} | |
function invoke(method) { | |
var args = $A(arguments).slice(1); | |
return this.map(function(value) { | |
return value[method].apply(value, args); | |
}); | |
} | |
function max(iterator, context) { | |
iterator = iterator || Prototype.K; | |
var result; | |
this.each(function(value, index) { | |
value = iterator.call(context, value, index); | |
if (result == null || value >= result) | |
result = value; | |
}); | |
return result; | |
} | |
function min(iterator, context) { | |
iterator = iterator || Prototype.K; | |
var result; | |
this.each(function(value, index) { | |
value = iterator.call(context, value, index); | |
if (result == null || value < result) | |
result = value; | |
}); | |
return result; | |
} | |
function partition(iterator, context) { | |
iterator = iterator || Prototype.K; | |
var trues = [], falses = []; | |
this.each(function(value, index) { | |
(iterator.call(context, value, index) ? | |
trues : falses).push(value); | |
}); | |
return [trues, falses]; | |
} | |
function pluck(property) { | |
var results = []; | |
this.each(function(value) { | |
results.push(value[property]); | |
}); | |
return results; | |
} | |
function reject(iterator, context) { | |
var results = []; | |
this.each(function(value, index) { | |
if (!iterator.call(context, value, index)) | |
results.push(value); | |
}); | |
return results; | |
} | |
function sortBy(iterator, context) { | |
return this.map(function(value, index) { | |
return { | |
value: value, | |
criteria: iterator.call(context, value, index) | |
}; | |
}).sort(function(left, right) { | |
var a = left.criteria, b = right.criteria; | |
return a < b ? -1 : a > b ? 1 : 0; | |
}).pluck('value'); | |
} | |
function toArray() { | |
return this.map(); | |
} | |
function zip() { | |
var iterator = Prototype.K, args = $A(arguments); | |
if (Object.isFunction(args.last())) | |
iterator = args.pop(); | |
var collections = [this].concat(args).map($A); | |
return this.map(function(value, index) { | |
return iterator(collections.pluck(index)); | |
}); | |
} | |
function size() { | |
return this.toArray().length; | |
} | |
function inspect() { | |
return '#<Enumerable:' + this.toArray().inspect() + '>'; | |
} | |
return { | |
each: each, | |
eachSlice: eachSlice, | |
all: all, | |
every: all, | |
any: any, | |
some: any, | |
collect: collect, | |
map: collect, | |
detect: detect, | |
findAll: findAll, | |
select: findAll, | |
filter: findAll, | |
grep: grep, | |
include: include, | |
member: include, | |
inGroupsOf: inGroupsOf, | |
inject: inject, | |
invoke: invoke, | |
max: max, | |
min: min, | |
partition: partition, | |
pluck: pluck, | |
reject: reject, | |
sortBy: sortBy, | |
toArray: toArray, | |
entries: toArray, | |
zip: zip, | |
size: size, | |
inspect: inspect, | |
find: detect | |
}; | |
})(); | |
function $A(iterable) { | |
if (!iterable) return []; | |
if ('toArray' in Object(iterable)) return iterable.toArray(); | |
var length = iterable.length || 0, results = new Array(length); | |
while (length--) results[length] = iterable[length]; | |
return results; | |
} | |
function $w(string) { | |
if (!Object.isString(string)) return []; | |
string = string.strip(); | |
return string ? string.split(/\s+/) : []; | |
} | |
Array.from = $A; | |
(function() { | |
var arrayProto = Array.prototype, | |
slice = arrayProto.slice, | |
_each = arrayProto.forEach; // use native browser JS 1.6 implementation if available | |
function each(iterator, context) { | |
for (var i = 0, length = this.length >>> 0; i < length; i++) { | |
if (i in this) iterator.call(context, this[i], i, this); | |
} | |
} | |
if (!_each) _each = each; | |
function clear() { | |
this.length = 0; | |
return this; | |
} | |
function first() { | |
return this[0]; | |
} | |
function last() { | |
return this[this.length - 1]; | |
} | |
function compact() { | |
return this.select(function(value) { | |
return value != null; | |
}); | |
} | |
function flatten() { | |
return this.inject([], function(array, value) { | |
if (Object.isArray(value)) | |
return array.concat(value.flatten()); | |
array.push(value); | |
return array; | |
}); | |
} | |
function without() { | |
var values = slice.call(arguments, 0); | |
return this.select(function(value) { | |
return !values.include(value); | |
}); | |
} | |
function reverse(inline) { | |
return (inline === false ? this.toArray() : this)._reverse(); | |
} | |
function uniq(sorted) { | |
return this.inject([], function(array, value, index) { | |
if (0 == index || (sorted ? array.last() != value : !array.include(value))) | |
array.push(value); | |
return array; | |
}); | |
} | |
function intersect(array) { | |
return this.uniq().findAll(function(item) { | |
return array.detect(function(value) { return item === value }); | |
}); | |
} | |
function clone() { | |
return slice.call(this, 0); | |
} | |
function size() { | |
return this.length; | |
} | |
function inspect() { | |
return '[' + this.map(Object.inspect).join(', ') + ']'; | |
} | |
function indexOf(item, i) { | |
i || (i = 0); | |
var length = this.length; | |
if (i < 0) i = length + i; | |
for (; i < length; i++) | |
if (this[i] === item) return i; | |
return -1; | |
} | |
function lastIndexOf(item, i) { | |
i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; | |
var n = this.slice(0, i).reverse().indexOf(item); | |
return (n < 0) ? n : i - n - 1; | |
} | |
function concat() { | |
var array = slice.call(this, 0), item; | |
for (var i = 0, length = arguments.length; i < length; i++) { | |
item = arguments[i]; | |
if (Object.isArray(item) && !('callee' in item)) { | |
for (var j = 0, arrayLength = item.length; j < arrayLength; j++) | |
array.push(item[j]); | |
} else { | |
array.push(item); | |
} | |
} | |
return array; | |
} | |
Object.extend(arrayProto, Enumerable); | |
if (!arrayProto._reverse) | |
arrayProto._reverse = arrayProto.reverse; | |
Object.extend(arrayProto, { | |
_each: _each, | |
clear: clear, | |
first: first, | |
last: last, | |
compact: compact, | |
flatten: flatten, | |
without: without, | |
reverse: reverse, | |
uniq: uniq, | |
intersect: intersect, | |
clone: clone, | |
toArray: clone, | |
size: size, | |
inspect: inspect | |
}); | |
var CONCAT_ARGUMENTS_BUGGY = (function() { | |
return [].concat(arguments)[0][0] !== 1; | |
})(1,2) | |
if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; | |
if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; | |
if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; | |
})(); | |
function $H(object) { | |
return new Hash(object); | |
}; | |
var Hash = Class.create(Enumerable, (function() { | |
function initialize(object) { | |
this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); | |
} | |
function _each(iterator) { | |
for (var key in this._object) { | |
var value = this._object[key], pair = [key, value]; | |
pair.key = key; | |
pair.value = value; | |
iterator(pair); | |
} | |
} | |
function set(key, value) { | |
return this._object[key] = value; | |
} | |
function get(key) { | |
if (this._object[key] !== Object.prototype[key]) | |
return this._object[key]; | |
} | |
function unset(key) { | |
var value = this._object[key]; | |
delete this._object[key]; | |
return value; | |
} | |
function toObject() { | |
return Object.clone(this._object); | |
} | |
function keys() { | |
return this.pluck('key'); | |
} | |
function values() { | |
return this.pluck('value'); | |
} | |
function index(value) { | |
var match = this.detect(function(pair) { | |
return pair.value === value; | |
}); | |
return match && match.key; | |
} | |
function merge(object) { | |
return this.clone().update(object); | |
} | |
function update(object) { | |
return new Hash(object).inject(this, function(result, pair) { | |
result.set(pair.key, pair.value); | |
return result; | |
}); | |
} | |
function toQueryPair(key, value) { | |
if (Object.isUndefined(value)) return key; | |
return key + '=' + encodeURIComponent(String.interpret(value)); | |
} | |
function toQueryString() { | |
return this.inject([], function(results, pair) { | |
var key = encodeURIComponent(pair.key), values = pair.value; | |
if (values && typeof values == 'object') { | |
if (Object.isArray(values)) { | |
var queryValues = []; | |
for (var i = 0, len = values.length, value; i < len; i++) { | |
value = values[i]; | |
queryValues.push(toQueryPair(key, value)); | |
} | |
return results.concat(queryValues); | |
} | |
} else results.push(toQueryPair(key, values)); | |
return results; | |
}).join('&'); | |
} | |
function inspect() { | |
return '#<Hash:{' + this.map(function(pair) { | |
return pair.map(Object.inspect).join(': '); | |
}).join(', ') + '}>'; | |
} | |
function clone() { | |
return new Hash(this); | |
} | |
return { | |
initialize: initialize, | |
_each: _each, | |
set: set, | |
get: get, | |
unset: unset, | |
toObject: toObject, | |
toTemplateReplacements: toObject, | |
keys: keys, | |
values: values, | |
index: index, | |
merge: merge, | |
update: update, | |
toQueryString: toQueryString, | |
inspect: inspect, | |
toJSON: toObject, | |
clone: clone | |
}; | |
})()); | |
Hash.from = $H; | |
Object.extend(Number.prototype, (function() { | |
function toColorPart() { | |
return this.toPaddedString(2, 16); | |
} | |
function succ() { | |
return this + 1; | |
} | |
function times(iterator, context) { | |
$R(0, this, true).each(iterator, context); | |
return this; | |
} | |
function toPaddedString(length, radix) { | |
var string = this.toString(radix || 10); | |
return '0'.times(length - string.length) + string; | |
} | |
function abs() { | |
return Math.abs(this); | |
} | |
function round() { | |
return Math.round(this); | |
} | |
function ceil() { | |
return Math.ceil(this); | |
} | |
function floor() { | |
return Math.floor(this); | |
} | |
return { | |
toColorPart: toColorPart, | |
succ: succ, | |
times: times, | |
toPaddedString: toPaddedString, | |
abs: abs, | |
round: round, | |
ceil: ceil, | |
floor: floor | |
}; | |
})()); | |
function $R(start, end, exclusive) { | |
return new ObjectRange(start, end, exclusive); | |
} | |
var ObjectRange = Class.create(Enumerable, (function() { | |
function initialize(start, end, exclusive) { | |
this.start = start; | |
this.end = end; | |
this.exclusive = exclusive; | |
} | |
function _each(iterator) { | |
var value = this.start; | |
while (this.include(value)) { | |
iterator(value); | |
value = value.succ(); | |
} | |
} | |
function include(value) { | |
if (value < this.start) | |
return false; | |
if (this.exclusive) | |
return value < this.end; | |
return value <= this.end; | |
} | |
return { | |
initialize: initialize, | |
_each: _each, | |
include: include | |
}; | |
})()); | |
var Ajax = { | |
getTransport: function() { | |
return Try.these( | |
function() {return new XMLHttpRequest()}, | |
function() {return new ActiveXObject('Msxml2.XMLHTTP')}, | |
function() {return new ActiveXObject('Microsoft.XMLHTTP')} | |
) || false; | |
}, | |
activeRequestCount: 0 | |
}; | |
Ajax.Responders = { | |
responders: [], | |
_each: function(iterator) { | |
this.responders._each(iterator); | |
}, | |
register: function(responder) { | |
if (!this.include(responder)) | |
this.responders.push(responder); | |
}, | |
unregister: function(responder) { | |
this.responders = this.responders.without(responder); | |
}, | |
dispatch: function(callback, request, transport, json) { | |
this.each(function(responder) { | |
if (Object.isFunction(responder[callback])) { | |
try { | |
responder[callback].apply(responder, [request, transport, json]); | |
} catch (e) { } | |
} | |
}); | |
} | |
}; | |
Object.extend(Ajax.Responders, Enumerable); | |
Ajax.Responders.register({ | |
onCreate: function() { Ajax.activeRequestCount++ }, | |
onComplete: function() { Ajax.activeRequestCount-- } | |
}); | |
Ajax.Base = Class.create({ | |
initialize: function(options) { | |
this.options = { | |
method: 'post', | |
asynchronous: true, | |
contentType: 'application/x-www-form-urlencoded', | |
encoding: 'UTF-8', | |
parameters: '', | |
evalJSON: true, | |
evalJS: true | |
}; | |
Object.extend(this.options, options || { }); | |
this.options.method = this.options.method.toLowerCase(); | |
if (Object.isHash(this.options.parameters)) | |
this.options.parameters = this.options.parameters.toObject(); | |
} | |
}); | |
Ajax.Request = Class.create(Ajax.Base, { | |
_complete: false, | |
initialize: function($super, url, options) { | |
$super(options); | |
this.transport = Ajax.getTransport(); | |
this.request(url); | |
}, | |
request: function(url) { | |
this.url = url; | |
this.method = this.options.method; | |
var params = Object.isString(this.options.parameters) ? | |
this.options.parameters : | |
Object.toQueryString(this.options.parameters); | |
if (!['get', 'post'].include(this.method)) { | |
params += (params ? '&' : '') + "_method=" + this.method; | |
this.method = 'post'; | |
} | |
if (params && this.method === 'get') { | |
this.url += (this.url.include('?') ? '&' : '?') + params; | |
} | |
this.parameters = params.toQueryParams(); | |
// try { | |
var response = new Ajax.Response(this); | |
if (this.options.onCreate) this.options.onCreate(response); | |
Ajax.Responders.dispatch('onCreate', this, response); | |
this.transport.open(this.method.toUpperCase(), this.url, | |
this.options.asynchronous); | |
if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); | |
this.transport.onreadystatechange = this.onStateChange.bind(this); | |
this.setRequestHeaders(); | |
this.body = this.method == 'post' ? (this.options.postBody || params) : null; | |
this.transport.send(this.body); | |
/* Force Firefox to handle ready state 4 for synchronous requests */ | |
if (!this.options.asynchronous && this.transport.overrideMimeType) | |
this.onStateChange(); | |
/* } | |
catch (e) { | |
this.dispatchException(e); | |
}*/ | |
}, | |
onStateChange: function() { | |
var readyState = this.transport.readyState; | |
if (readyState > 1 && !((readyState == 4) && this._complete)) | |
this.respondToReadyState(this.transport.readyState); | |
}, | |
setRequestHeaders: function() { | |
var headers = { | |
'X-Requested-With': 'XMLHttpRequest', | |
'X-Prototype-Version': Prototype.Version, | |
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' | |
}; | |
if (this.method == 'post') { | |
headers['Content-type'] = this.options.contentType + | |
(this.options.encoding ? '; charset=' + this.options.encoding : ''); | |
/* Force "Connection: close" for older Mozilla browsers to work | |
* around a bug where XMLHttpRequest sends an incorrect | |
* Content-length header. See Mozilla Bugzilla #246651. | |
*/ | |
if (this.transport.overrideMimeType && | |
(navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) | |
headers['Connection'] = 'close'; | |
} | |
if (typeof this.options.requestHeaders == 'object') { | |
var extras = this.options.requestHeaders; | |
if (Object.isFunction(extras.push)) | |
for (var i = 0, length = extras.length; i < length; i += 2) | |
headers[extras[i]] = extras[i+1]; | |
else | |
$H(extras).each(function(pair) { headers[pair.key] = pair.value }); | |
} | |
for (var name in headers) | |
this.transport.setRequestHeader(name, headers[name]); | |
}, | |
success: function() { | |
var status = this.getStatus(); | |
return !status || (status >= 200 && status < 300) || status == 304; | |
}, | |
getStatus: function() { | |
//try { | |
if (this.transport.status === 1223) return 204; | |
return this.transport.status || 0; | |
//} catch (e) { return 0 } | |
}, | |
respondToReadyState: function(readyState) { | |
var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); | |
if (state == 'Complete') { | |
//try { | |
this._complete = true; | |
(this.options['on' + response.status] | |
|| this.options['on' + (this.success() ? 'Success' : 'Failure')] | |
|| Prototype.emptyFunction)(response, response.headerJSON); | |
/*} catch (e) { | |
this.dispatchException(e); | |
}*/ | |
var contentType = response.getHeader('Content-type'); | |
if (this.options.evalJS == 'force' | |
|| (this.options.evalJS && this.isSameOrigin() && contentType | |
&& contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) | |
this.evalResponse(); | |
} | |
//try { | |
(this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); | |
Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); | |
/*} catch (e) { | |
this.dispatchException(e); | |
}*/ | |
if (state == 'Complete') { | |
this.transport.onreadystatechange = Prototype.emptyFunction; | |
} | |
}, | |
isSameOrigin: function() { | |
var m = this.url.match(/^\s*https?:\/\/[^\/]*/); | |
return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ | |
protocol: location.protocol, | |
domain: document.domain, | |
port: location.port ? ':' + location.port : '' | |
})); | |
}, | |
getHeader: function(name) { | |
try { | |
return this.transport.getResponseHeader(name) || null; | |
} catch (e) { return null; } | |
}, | |
evalResponse: function() { | |
try { | |
return eval((this.transport.responseText || '').unfilterJSON()); | |
} catch (e) { | |
this.dispatchException(e); | |
} | |
}, | |
dispatchException: function(exception) { | |
(this.options.onException || Prototype.emptyFunction)(this, exception); | |
Ajax.Responders.dispatch('onException', this, exception); | |
} | |
}); | |
Ajax.Request.Events = | |
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; | |
Ajax.Response = Class.create({ | |
initialize: function(request){ | |
this.request = request; | |
var transport = this.transport = request.transport, | |
readyState = this.readyState = transport.readyState; | |
if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { | |
this.status = this.getStatus(); | |
this.statusText = this.getStatusText(); | |
this.responseText = String.interpret(transport.responseText); | |
this.headerJSON = this._getHeaderJSON(); | |
} | |
if (readyState == 4) { | |
var xml = transport.responseXML; | |
this.responseXML = Object.isUndefined(xml) ? null : xml; | |
this.responseJSON = this._getResponseJSON(); | |
} | |
}, | |
status: 0, | |
statusText: '', | |
getStatus: Ajax.Request.prototype.getStatus, | |
getStatusText: function() { | |
try { | |
return this.transport.statusText || ''; | |
} catch (e) { return '' } | |
}, | |
getHeader: Ajax.Request.prototype.getHeader, | |
getAllHeaders: function() { | |
try { | |
return this.getAllResponseHeaders(); | |
} catch (e) { return null } | |
}, | |
getResponseHeader: function(name) { | |
return this.transport.getResponseHeader(name); | |
}, | |
getAllResponseHeaders: function() { | |
return this.transport.getAllResponseHeaders(); | |
}, | |
_getHeaderJSON: function() { | |
var json = this.getHeader('X-JSON'); | |
if (!json) return null; | |
json = decodeURIComponent(escape(json)); | |
try { | |
return json.evalJSON(this.request.options.sanitizeJSON || | |
!this.request.isSameOrigin()); | |
} catch (e) { | |
this.request.dispatchException(e); | |
} | |
}, | |
_getResponseJSON: function() { | |
var options = this.request.options; | |
if (!options.evalJSON || (options.evalJSON != 'force' && | |
!(this.getHeader('Content-type') || '').include('application/json')) || | |
this.responseText.blank()) | |
return null; | |
try { | |
return this.responseText.evalJSON(options.sanitizeJSON || | |
!this.request.isSameOrigin()); | |
} catch (e) { | |
this.request.dispatchException(e); | |
} | |
} | |
}); | |
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); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Limechat Quake Notifier by whiteleaf | |
// | |
// 2011/03/28 Ver.1.0 | |
// | |
LimeExt.Require("library/prototype4lime.js"); | |
LimeExt.Require("library/socket.js"); | |
LimeExt.Require("library/ini.js"); | |
var debug = false; | |
var Quake = { | |
default_min_shindo: 3, // これ以上の震度だけ表示する(デフォ値、チャンネルごとに設定可能) | |
arguments_order_prefix: "quake", // コマンド名 | |
arguments_spliter: ">", // コマンドの区切り文字 | |
setting_filename: "quake_setting_" + escape_filename(name) + ".ini" // 設定ファイル名(プロファイル別管理) | |
}; | |
Quake.version = "1.1"; | |
var P2PQuakePlugin = Class.create(Socket, { | |
port: (debug ? 7777 : 6918), | |
retry_limit: 10, | |
retry_interval: 2000, | |
initialize: function($super, options) { | |
$super(); | |
this.connected = false; | |
this.retry_count = 0; | |
this.quited = false; | |
this.data_buffer = ""; | |
this.options = { | |
name: "P2PQuake Plugin for Limechat", | |
onReceive: function() {} | |
}; | |
Object.extend(this.options, options || {}); | |
this.reconnect(30 * 1000 + Math.floor(Math.random() * 10 * 1000)); | |
}, | |
connectToP2PQuakeCliant: function() { | |
if (!this.connected) { | |
this.connect("localhost", this.port); | |
if (this.retry_count-- > 0) { | |
setTimeout(this.connectToP2PQuakeCliant.bind(this), this.retry_interval); | |
} | |
else { | |
log("[LQH] Error: P2P地震情報クライアントに接続出来ませんでした。" + | |
"再接続には quake>reconnect を使用してください"); | |
} | |
} | |
}, | |
reconnect: function(interval) { | |
if (!interval) { | |
interval = this.retry_interval | |
} | |
if (this.connected || this.retry_count > 0) return; | |
this.retry_count = this.retry_limit; | |
setTimeout(this.connectToP2PQuakeCliant.bind(this), interval); | |
}, | |
onConnect: function() { | |
this.connected = true; | |
this.retry_count = 0; | |
this.send("JOIN", this.options.name); | |
}, | |
onClose: function() { | |
this.connected = false; | |
log("[LQN] Error: P2P地震情報クライアントとの接続が切れました"); | |
}, | |
onError: function($super) { | |
$super(); | |
this.connected = false; | |
}, | |
onDataArrival: function(total) { | |
this.data_buffer = this.getData(); | |
var operation = this.data_buffer.substring(0, 4); | |
var data = (this.data_buffer.length > 5 ? this.data_buffer.substring(5).replace(/[\r\n]/g, "") : ""); | |
this.options.onReceive.apply(this, [operation, data]); | |
this.data_buffer = ""; | |
}, | |
quit: function() { | |
if (!this.quited) { | |
this.close(); | |
this.quited = true; | |
} | |
}, | |
send: function(operation, data) { | |
this.sendData(operation.toUpperCase() + " " + data + "\r\n"); | |
} | |
}); | |
Quake.Notifier = Class.create(P2PQuakePlugin, { | |
initialize: function($super) { | |
$super({ | |
name: "Limechat Quake Notifier (Version " + Quake.version+ ")", | |
onReceive: this.receive | |
}); | |
this.load_setting(); | |
log(this.options.name); | |
}, | |
test: function() { | |
this.receive("SVER", "P2P地震情報 for Peer(Beta 3/Revision 1012)"); | |
//this.receive("QUAK", "28日14時38分,3,0,4,秋田県内陸南部,20km,4.1,0,N39.6,E140.5,気象庁:-秋田県,+3,*大仙市,*仙北市,+1,*秋田市,*由利本荘市,*大館市,*北秋田市,*秋田美郷町,-岩手県,+1,*矢巾町"); | |
this.receive("QUAK", "28日20時08分,3,0,4,茨城県北部,20km,4.1,0,N36.7,E140.6,気象庁:-茨城県,+3,*常陸太田市,+2,*日立市,*高萩市,*大子町,*常陸大宮市,+1,*水戸市,*北茨城市,*笠間市,*ひたちなか市,*那珂市,*城里町,*小美玉市,*土浦市,*石岡市,*つくば市,*茨城鹿嶋市,*坂東市,*筑西市,*かすみがうら市,*桜川市,*鉾田市,*常総市,-福島県,+2,*矢祭町,*平田村,*楢葉町,+1,*白河市,*二本松市,*鏡石町,*天栄村,*棚倉町,*玉川村,*浅川町,*古殿町,*小野町,*田村,*福島伊達市,*いわき市,*飯舘村,-宮城県,+1,*岩沼市,-栃木県,+1,*宇都宮市,*栃木市,*鹿沼市,*芳賀町,*那須烏山市,*栃木那珂川町,-埼玉県,+1,*宮代町"); | |
//this.receive("QUAK", "28日23時32分,2,0,4,新潟県中越地方,10km,1.9,0,N37.0,E138.6,気象庁:-長野県,+2,*栄村"); | |
this.receive("OPTI"); | |
this.receive("EEW1", "1"); | |
this.receive("EEW1", "0"); | |
}, | |
commandline: function(prefix, channel, text) { | |
new Quake.Command(prefix, channel, text, this); | |
}, | |
receive: function(operation, data) { | |
switch (operation) { | |
case "SVER": | |
log("[LQN] connected to " + data); | |
break; | |
case "QUAK": | |
this.notify_quak(data); | |
break; | |
case "EEW1": | |
if (data == "0") { | |
this.channels.each(function(ch) { | |
send(ch, "<bold><color red>!!!緊急地震速報!!!<stop>"); | |
}); | |
} | |
break; | |
case "OPTI": | |
log(this.options.name); | |
log("操作方法に関しては " + Quake.arguments_order_prefix + | |
Quake.arguments_spliter + "help を参照して下さい"); | |
break; | |
default: | |
break; | |
} | |
}, | |
cache_summary: {}, | |
cache_detail: "", | |
latest_summary: "", | |
latest_detail: "", | |
notify_quak: function(data) { | |
var split_data = data.split(":"); | |
var summary = split_data[0], detail = split_data[1]; | |
if (detail.length == 0) { | |
return; | |
} | |
var result = this.get_quak_summary(summary); | |
if (result.elements.seismic_intensity == 0 || this.cache_summary[result.elements.date]) { | |
return; | |
} | |
if (result.elements.eq_position == "--") { | |
// 「地震速報」では震源・規模情報が配信されずに、速報震度と各地震度情報しか配信されない。 | |
// 第二報以降の「地震情報」「震源情報」で詳細が発表されるため、第一報の各地震度のみ保存して | |
// 第二報以降の震度概要と統合する | |
this.cache_detail = detail; | |
return; | |
} | |
if (this.cache_detail != "") { | |
detail = this.cache_detail; | |
this.latest_detail = detail; | |
this.cache_detail = ""; | |
} | |
this.cache_summary[result.elements.date] = true; | |
var summary_message = result.message; | |
this.latest_summary = summary_message; | |
this.channels.each(function(ch) { | |
var min = (this.min_shindo[ch] ? this.min_shindo[ch] : Quake.default_min_shindo); | |
var detail_messages = this.get_quak_detail(detail, min); | |
if (detail_messages.length > 0) { | |
send(ch, summary_message); | |
detail_messages.each(function(msg) { | |
send(ch, msg); | |
}); | |
} | |
}, this); | |
}, | |
get_quak_summary: function(summary) { | |
var split_data = summary.split(","); | |
var elements = { | |
date: split_data[0], | |
seismic_intensity: split_data[1], | |
eq_position: split_data[4], | |
eq_deep: split_data[5], | |
magnitude: split_data[6], | |
eq_pos_n: split_data[8], | |
eq_pos_e: split_data[9], | |
vender: split_data[10] | |
}; | |
if (elements.eq_position == "") { | |
elements.eq_position = "--"; | |
} | |
if (elements.magnitude == "-1.0") { | |
elements.magnitude = "--"; | |
} | |
var template = new Template( | |
"<bold>地震情報<stop> #{date} (最大震度#{seismic_intensity}) [震源]#{eq_position} [規模]M#{magnitude}"); | |
return { | |
elements: elements, | |
message: template.evaluate(elements) | |
}; | |
}, | |
get_quak_detail: function(detail, min_shindo) { | |
var result = {}; | |
var prefecture = "", seismic_intensity = 0; | |
detail.split(",").each(function(value) { | |
var op = value.substring(0, 1); | |
var info = value.substring(1); | |
switch (op) { | |
case "-": | |
prefecture = info; | |
if (!result[prefecture]) { | |
result[prefecture] = {}; | |
} | |
break; | |
case "+": | |
seismic_intensity = info; | |
result[prefecture][seismic_intensity] = []; | |
break; | |
case "*": | |
result[prefecture][seismic_intensity].push(info); | |
break; | |
default: | |
break; | |
} | |
}, this); | |
var messages = []; | |
for (prefecture in result) { | |
if (prefecture.length > 0) { | |
var cities = []; | |
for (seismic_intensity in result[prefecture]) { | |
if (seismic_intensity >= min_shindo) { | |
cities.push("震度" + seismic_intensity + ":" + | |
result[prefecture][seismic_intensity].join(" ")); | |
} | |
} | |
if (cities.length > 0) { | |
messages.push("[" + prefecture + "]" + cities.join(" ")); | |
} | |
} | |
} | |
return messages; | |
}, | |
load_setting: function() { | |
this.channels = []; | |
this.min_shindo = {}; | |
this.setting = new Ini(Quake.setting_filename); | |
if (!this.setting.load()) { | |
return; | |
} | |
if (this.setting.data.global.channels != "") { | |
this.channels = this.setting.data.global.channels.split(","); | |
} | |
for (var ch in this.setting.data.min_shindo) { | |
if (/^[1-7]$/.test(this.setting.data.min_shindo[ch])) { | |
this.min_shindo[ch] = this.setting.data.min_shindo[ch]; | |
} | |
else { | |
this.min_shindo[ch] = Quake.default_min_shindo; | |
} | |
} | |
}, | |
save_setting: function() { | |
this.setting.data.global.channels = this.channels.join(","); | |
this.setting.data.min_shindo = this.min_shindo; | |
this.setting.save(); | |
} | |
}); | |
Quake.Console = Class.create({ | |
initialize: function(channel) { | |
this.channel = channel; | |
}, | |
send: function(message) { | |
send(this.channel, message); | |
}, | |
action: function(message) { | |
action(this.channel, message); | |
}, | |
privmsg: function(message) { | |
sendRaw("privmsg " + this.channel + " " + message); | |
}, | |
information: function(message) { | |
this.send("[LQN] " + message); | |
} | |
}); | |
Quake.Command = Class.create({ | |
commands: { | |
min: ["[1-7](表示震度の閾値)"], latest: [], boot: [], kill: [], reconnect: []/*, test: []*/ | |
}, | |
initialize: function(prefix, channel, text, notifier) { | |
var args = text.toLowerCase().split(Quake.arguments_spliter); | |
if (args.shift() != Quake.arguments_order_prefix) { | |
return; | |
} | |
this.notifier = notifier; | |
this.channel = channel; | |
this.owner = (prefix.nick == myNick); | |
this.is_boot = this.is_valid_channel(channel); | |
this.console = new Quake.Console(channel); | |
var order = args.shift(); | |
if (!order) { | |
this.help(); | |
return; | |
} | |
if (order == "help") { | |
this.help(); | |
return; | |
} | |
if (this.commands[order]) { | |
if (args.length < this.commands[order].length) { | |
this.console.information("コマンドの引数が足りません。 " + Quake.arguments_order_prefix + | |
Quake.arguments_spliter + "help を参照して下さい"); | |
return; | |
} | |
this[order](args); | |
return; | |
} | |
if (!this.is_boot && !this.owner) return; | |
this.console.information("コマンドが間違っています。 " + Quake.arguments_order_prefix + | |
Quake.arguments_spliter + "help を参照して下さい"); | |
}, | |
is_valid_channel: function(channel) { | |
return this.notifier.channels.include(channel); | |
}, | |
test: function() { | |
this.notifier.test(); | |
}, | |
// quake>boot | |
// for owner | |
boot: function() { | |
if (!this.owner) return; | |
if (!this.is_boot) { | |
this.notifier.channels.push(this.channel); | |
this.console.information("このチャンネルで QuakeNotifier を有効にしました"); | |
this.notifier.save_setting(); | |
} | |
else { | |
this.console.information("すでに有効済みです"); | |
} | |
}, | |
// quake>kill | |
// for owner | |
kill: function() { | |
if (!this.owner) return; | |
if (!this.is_boot) { | |
this.help(); | |
return; | |
} | |
var index = this.notifier.channels.indexOf(this.channel); | |
if (index >= 0) { | |
this.notifier.channels.splice(index, 1); | |
this.console.information("このチャンネルで QuakeNotifier を無効にしました"); | |
this.notifier.save_setting(); | |
} | |
}, | |
// quake>min>[1-7] | |
// for owner | |
min: function(args) { | |
if (!this.owner) return; | |
if (!this.is_boot) { | |
this.help(); | |
return; | |
} | |
if (/^[1-7]$/.test(args[0])) { | |
this.notifier.min_shindo[this.channel] = args[0]; | |
this.console.information("このチャンネルでの閾値を " + args[0] + " 以上にしました"); | |
this.notifier.save_setting(); | |
} | |
else { | |
this.console.information("震度の閾値は 1~7 の間で入力して下さい"); | |
} | |
}, | |
// quake>latest>&option shindo | |
latest: function(args) { | |
var min; | |
if (args.length == 1 && /^[1-7]$/.test(args[0])) { | |
min = args[0]; | |
} | |
else { | |
min = (this.notifier.min_shindo[this.channel] ? this.notifier.min_shindo[this.channel] : Quake.default_min_shindo); | |
} | |
if (this.notifier.latest_summary != "" && this.notifier.latest_detail != "") { | |
var detail_messages = this.notifier.get_quak_detail(this.notifier.latest_detail, min); | |
if (detail_messages.length > 0) { | |
this.console.information(this.notifier.latest_summary) | |
detail_messages.each(function(msg) { | |
this.console.information(msg); | |
}, this); | |
} | |
} | |
}, | |
// quake>reconnect | |
// for owner | |
reconnect: function() { | |
if (!this.owner) return; | |
if (!this.is_boot) { | |
this.help(); | |
return; | |
} | |
if (!this.notifier.connected) { | |
this.console.information("P2P地震情報クライアントに再接続を試みます"); | |
this.notifier.reconnect(); | |
} | |
else { | |
this.console.information("接続済みです"); | |
} | |
}, | |
// quake>help | |
help: function() { | |
if (!this.owner) return; | |
if (!this.is_boot) { | |
this.console.information("このチャンネルで有効にするには " + Quake.arguments_order_prefix + | |
Quake.arguments_spliter + "boot を実行して下さい"); | |
return; | |
} | |
var messages = []; | |
for (var order in this.commands) { | |
msg = Quake.arguments_order_prefix + Quake.arguments_spliter + order; | |
if (this.commands[order].length > 0) { | |
msg += Quake.arguments_spliter + this.commands[order].join(Quake.arguments_spliter); | |
} | |
messages.push(msg); | |
} | |
this.console.send(messages.join(" | ") + " " + (this.notifier.connected ? "接続中" : "未接続")); | |
} | |
}); | |
var quake = null; | |
function event::onLoad() { | |
quake = new Quake.Notifier; | |
//quake = new Quake.NotifierTest; | |
} | |
function event::onUnload() { | |
if (quake) { | |
quake.quit(); | |
} | |
} | |
function event::onChannelText(prefix, channel, text) { | |
quake.commandline(prefix, channel, text); | |
} | |
// | |
// ファイル名に使えない文字等をエスケープ | |
// | |
function escape_filename(filename) { | |
var pattern = [[":", "colon"], ["?", "question"], ["/", "slash"]]; | |
for (var i in pattern) { | |
filename = filename.replace(pattern[i][0], pattern[i][1]); | |
} | |
return encodeURI(filename); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Socket library | |
// require: OSWINSOCK.dll | |
// http://www.ostrosoft.com/oswinsck.asp | |
// | |
var Socket = Class.create({ | |
STATE: { | |
CLOSED: 0, OPEN: 1, LISTENING: 2, CONNECTION_PENDING: 3, RESOLVING_HOST: 4, | |
HOST_RESOLVED: 5, CONNECTING: 6, CONNECTED: 7, CLOSING: 8, ERROR: 9 | |
}, | |
initialize: function() { | |
this.regist_event(); | |
this.winsock = new ActiveXObject("OSWINSCK.Winsock"); | |
LimeExt.ConnectObject(this.winsock, this); | |
}, | |
connect: function(host, port) { | |
return this.winsock.Connect(host, port); | |
}, | |
close: function() { | |
this.winsock.CloseWinsock(); | |
}, | |
accept: function(request_id) { | |
return this.winsock.Accept(request_id); | |
}, | |
bind: function(local_port, local_ip) { | |
if (!local_port) local_port = LimeExt.Empty; | |
if (!local_ip) local_ip = LimeExt.Empty; | |
return this.winsock.Bind(local_port, local_ip); | |
}, | |
getData: function() { | |
return this.winsock.GetDataBuffer(); | |
}, | |
getDataBuffer: function() { // alias getData | |
return this.getData(); | |
}, | |
listen: function() { | |
return this.winsock.Listen(); | |
}, | |
sendData: function(data) { | |
this.winsock.SendData(data); | |
}, | |
// event handlers | |
onClose: function() {}, | |
onConnect: function() {}, | |
onConnectionRequest: function(request_id) {}, | |
onDataArrival: function(bytes_total) {}, | |
onError: function(number, description, scode, source, help_file, help_context, cancel_display) { | |
log("SocketError(" + number + ") : " + description); | |
}, | |
onSendComplete: function() {}, | |
onSendProgress: function(bytes_sent, bytes_remaining) {}, | |
onStatusChanged: function(status) {}, | |
events: ["Close", "Connect", "ConnectionRequest", "DataArrival", "Error", "SendComplete", | |
"SendProgress", "StatusChanged"], | |
regist_event: function() { | |
this.events.each(function(event_name) { | |
this["On" + event_name] = function() { | |
this["on" + event_name].apply(this, arguments); | |
}.bind(this); | |
}.bind(this)); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment