Skip to content

Instantly share code, notes, and snippets.

@chrif
Created March 19, 2012 00:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrif/2087654 to your computer and use it in GitHub Desktop.
Save chrif/2087654 to your computer and use it in GitHub Desktop.
Mists
var Mists = {};
Mists.ready = function () {
var user = new Mists.User();
user.login(function () {
var wall = new Mists.Wall(user);
var wallButton = Mists.Menu.getInstance().getWallButton();
if (wallButton.length) {
wallButton.attr('href', wall.getUrl());
wall.checkNewMessages(function (data) {
Mists.UI.blink(wallButton.find('img'));
});
}
});
Mists.UI.addTodaysActiveTopics();
};
Mists.Util = {
implement:function (instance, interface_) {
for (var member in interface_) {
if (interface_.hasOwnProperty(member)) {
if (typeof instance[member] != typeof interface_[member]) {
$.error("object failed to implement interface member " + member);
return false;
}
}
}
return true;
},
bind:function (self, obj) {
var slice = [].slice,
args = slice.call(arguments, 2),
nop = function () {
},
bound = function () {
return self.apply(this instanceof nop ? this : ( obj || {} ),
args.concat(slice.call(arguments)));
};
nop.prototype = self.prototype;
bound.prototype = new nop();
return bound;
},
unserialize:function (str) {
var obj = {};
if (str.indexOf('=') == -1) {
return obj;
}
$.each(str.split('&'), function () {
var pair = this.split('=');
obj[pair[0]] = pair[1];
});
return obj;
}
};
Mists.Interface = {
Page:{
getUrl:function () {
},
isCurrent:function () {
},
fetch:function (callback) {
}
}
};
Mists.Cookie = {
read:function (name) {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; ++i) {
var pair = cookies[i].split('=');
if ($.trim(pair[0]) == name) {
return decodeURIComponent(pair[1]);
}
}
return '';
},
erase:function (name) {
this.create(name, "", -1);
},
create:function (name, value, days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toGMTString();
}
document.cookie = name + "=" + encodeURIComponent(value) + expires + "; path=/";
}
};
Mists.UI = {
addTodaysActiveTopics:function () {
$('#userlinks ul').prepend('<li><a href="/search?search_id=activetopics">Today\'s active topics</a></li>');
},
blink:function (elements) {
$(elements).each(function () {
var element = $(this);
if (element.data('blink_timer')) {
window.clearInterval(element.data('blink_timer'));
}
element.css('visibility', 'visible');
element.data('blink_timer', window.setInterval(function () {
if (element.css('visibility') == 'visible') {
element.css('visibility', 'hidden');
} else {
element.css('visibility', 'visible');
}
}, 500));
});
}
};
Mists.Config = (function () {
var instance, _static;
function Config() {
var _public = $.extend(this, {
set:function (name, value) {
_private.data[name] = value;
_private.save();
},
get:function (name) {
return _private.data[name];
},
erase:function () {
Mists.Cookie.erase(_static.NAME);
},
getData:function () {
return _private.data;
}
});
var _private = {
data:{},
save:function () {
Mists.Cookie.create(_static.NAME, Mists.PHP.serialize(_private.data), 365);
},
read:function () {
try {
return Mists.PHP.unserialize(Mists.Cookie.read(_static.NAME));
} catch (e) {
// for backward compatibility
return Mists.Util.unserialize(Mists.Cookie.read(_static.NAME));
}
}
};
_private.data = _private.read();
}
_static = {
LAST_WM_ID_READ:'last_wm_id_read',
USER_ID:'user_id',
NAME:'mists_config',
getInstance:function () {
if (instance === undefined) {
instance = new Config();
}
return instance;
}
};
return _static;
}());
Mists.Wall = function (user) {
var _public = $.extend(this, {
getUrl:function () {
return '/u' + user.getUserId() + 'wall';
},
fetch:function (callback) {
$.get(_public.getUrl(), callback);
},
isCurrent:function () {
return location.href.indexOf(_public.getUrl()) != -1;
},
checkNewMessages:function (newMessageCallback) {
if (_public.isCurrent()) {
var data = _private.parseFrom(document.body);
_private.setLastMessageId(data.last_message_id);
} else {
_public.fetch(function (response) {
var data = _private.parseFrom(response);
var new_message_id = data.last_message_id;
if (new_message_id) {
var old_message_id = _private.getLastMessageId();
if (!old_message_id) {
_private.setLastMessageId(new_message_id);
} else if (old_message_id != new_message_id) {
newMessageCallback(data);
}
}
});
}
}
});
var _private = {
parseFrom:function (data) {
// parse last message id
var href = $('.message-footer:eq(0) a', data).attr('href');
var pattern = /\d+$/;
var last_message_id = pattern.test(href) ? href.match(pattern)[0] : 0;
return {
last_message_id:last_message_id
};
},
getLastMessageId:function () {
return Mists.Config.getInstance().get(Mists.Config.LAST_WM_ID_READ);
},
setLastMessageId:function (id) {
Mists.Config.getInstance().set(Mists.Config.LAST_WM_ID_READ, id);
}
};
Mists.Util.implement(_public, Mists.Interface.Page);
};
Mists.Profile = function () {
var _public = $.extend(this, {
getUrl:function () {
return '/profile?mode=editprofile&page_profil=informations';
},
isCurrent:function () {
return location.href.indexOf(_public.getUrl()) != -1;
},
fetch:function (callback) {
$.get(_public.getUrl(), callback);
},
parse:function (callback) {
if (_public.isCurrent()) {
callback(_private.parseFrom(document.body));
} else {
_public.fetch(function (response) {
callback(_private.parseFrom(response));
});
}
}
});
var _private = {
parseFrom:function (data) {
return {
user_id:$('input[name=user_id]', data).val()
};
}
};
Mists.Util.implement(_public, Mists.Interface.Page);
};
Mists.User = function () {
var _public = $.extend(this, {
getUserId:function () {
return _private.user_id;
},
logout:function () {
_private.setUserId(null);
},
login:function (successCallback) {
var _success = function () {
Mists.Menu.getInstance().getLogoutButton().off('click.mists').on('click.mists', _public.logout);
successCallback();
};
_private.cookieAuth();
if (_private.isLoggedIn()) {
_success();
} else if (_private.isProbablyLoggedIn()) {
_private.ajaxAuth(function () {
if (_private.isLoggedIn()) {
_success();
}
});
}
}
});
var _private = {
user_id:null,
updateConfig:function () {
if (Mists.Config.getInstance().get(Mists.Config.USER_ID) != _private.user_id) {
Mists.Config.getInstance().erase();
}
if (_private.isLoggedIn()) {
Mists.Config.getInstance().set(Mists.Config.USER_ID, _private.user_id);
}
},
setUserId:function (user_id) {
_private.user_id = user_id;
_private.updateConfig();
},
cookieAuth:function () {
try {
var cookie = Mists.Cookie.read('fa_' + location.host.replace(/\./g, '_') + '_data');
_private.setUserId(Mists.PHP.unserialize(cookie)['userid']);
} catch (e) {
}
},
ajaxAuth:function (callback) {
var profile = new Mists.Profile();
profile.parse(function (data) {
_private.setUserId(data.user_id);
callback();
});
},
isProbablyLoggedIn:function () {
return !!(_private.user_id || Mists.Menu.getInstance().getLogoutButton().length);
},
isLoggedIn:function () {
return !!_private.user_id;
}
};
};
Mists.Menu = (function () {
var instance, _static;
function Menu() {
var _public = $.extend(this, {
getLogoutButton:function () {
return _private.logoutButton;
},
getWallButton:function () {
return _private.wallButton;
}
});
var _private = {
menu:$('#submenu'),
wallButton:null,
logoutButton:null
};
_private.wallButton = _private.menu.find('a[href$=mists_vm]');
_private.logoutButton = _private.menu.find('#logout');
}
_static = {
getInstance:function () {
if (instance === undefined) {
instance = new Menu();
}
return instance;
}
};
return _static;
}());
Mists.PHP = {
serialize:function (mixed_value) {
// http://kevin.vanzonneveld.net
// + original by: Arpad Ray (mailto:arpad@php.net)
// + improved by: Dino
// + bugfixed by: Andrej Pavlovic
// + bugfixed by: Garagoth
// + input by: DtTvB (http://dt.in.th/2008-09-16.string-length-in-bytes.html)
// + bugfixed by: Russell Walker (http://www.nbill.co.uk/)
// + bugfixed by: Jamie Beck (http://www.terabit.ca/)
// + input by: Martin (http://www.erlenwiese.de/)
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net/)
// + improved by: Le Torbi (http://www.letorbi.de/)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net/)
// + bugfixed by: Ben (http://benblume.co.uk/)
// - depends on: utf8_encode
// % note: We feel the main purpose of this function should be to ease the transport of data between php & js
// % note: Aiming for PHP-compatibility, we have to translate objects to arrays
// * example 1: serialize(['Kevin', 'van', 'Zonneveld']);
// * returns 1: 'a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}'
// * example 2: serialize({firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'});
// * returns 2: 'a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}'
var _utf8Size = function (str) {
var size = 0,
i = 0,
l = str.length,
code = '';
for (i = 0; i < l; i++) {
code = str.charCodeAt(i);
if (code < 0x0080) {
size += 1;
} else if (code < 0x0800) {
size += 2;
} else {
size += 3;
}
}
return size;
};
var _getType = function (inp) {
var type = typeof inp,
match;
var key;
if (type === 'object' && !inp) {
return 'null';
}
if (type === "object") {
if (!inp.constructor) {
return 'object';
}
var cons = inp.constructor.toString();
match = cons.match(/(\w+)\(/);
if (match) {
cons = match[1].toLowerCase();
}
var types = ["boolean", "number", "string", "array"];
for (key in types) {
if (cons == types[key]) {
type = types[key];
break;
}
}
}
return type;
};
var type = _getType(mixed_value);
var val, ktype = '';
switch (type) {
case "function":
val = "";
break;
case "boolean":
val = "b:" + (mixed_value ? "1" : "0");
break;
case "number":
val = (Math.round(mixed_value) == mixed_value ? "i" : "d") + ":" + mixed_value;
break;
case "string":
val = "s:" + _utf8Size(mixed_value) + ":\"" + mixed_value + "\"";
break;
case "array":
case "object":
val = "a";
/*
if (type == "object") {
var objname = mixed_value.constructor.toString().match(/(\w+)\(\)/);
if (objname == undefined) {
return;
}
objname[1] = this.serialize(objname[1]);
val = "O" + objname[1].substring(1, objname[1].length - 1);
}
*/
var count = 0;
var vals = "";
var okey;
var key;
for (key in mixed_value) {
if (mixed_value.hasOwnProperty(key)) {
ktype = _getType(mixed_value[key]);
if (ktype === "function") {
continue;
}
okey = (key.match(/^[0-9]+$/) ? parseInt(key, 10) : key);
vals += this.serialize(okey) + this.serialize(mixed_value[key]);
count++;
}
}
val += ":" + count + ":{" + vals + "}";
break;
case "undefined":
// Fall-through
default:
// if the JS object has a property which contains a null value, the string cannot be unserialized by PHP
val = "N";
break;
}
if (type !== "object" && type !== "array") {
val += ";";
}
return val;
},
utf8_decode:function (str_data) {
// http://kevin.vanzonneveld.net
// + original by: Webtoolkit.info (http://www.webtoolkit.info/)
// + input by: Aman Gupta
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: Norman "zEh" Fuchs
// + bugfixed by: hitwork
// + bugfixed by: Onno Marsman
// + input by: Brett Zamir (http://brett-zamir.me)
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// * example 1: utf8_decode('Kevin van Zonneveld');
// * returns 1: 'Kevin van Zonneveld'
var tmp_arr = [],
i = 0,
ac = 0,
c1 = 0,
c2 = 0,
c3 = 0;
str_data += '';
while (i < str_data.length) {
c1 = str_data.charCodeAt(i);
if (c1 < 128) {
tmp_arr[ac++] = String.fromCharCode(c1);
i++;
} else if (c1 > 191 && c1 < 224) {
c2 = str_data.charCodeAt(i + 1);
tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = str_data.charCodeAt(i + 1);
c3 = str_data.charCodeAt(i + 2);
tmp_arr[ac++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return tmp_arr.join('');
},
unserialize:function (data) {
// http://kevin.vanzonneveld.net
// + original by: Arpad Ray (mailto:arpad@php.net)
// + improved by: Pedro Tainha (http://www.pedrotainha.com)
// + bugfixed by: dptr1988
// + revised by: d3x
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: Brett Zamir (http://brett-zamir.me)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: Chris
// + improved by: James
// + input by: Martin (http://www.erlenwiese.de/)
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: Le Torbi
// + input by: kilops
// + bugfixed by: Brett Zamir (http://brett-zamir.me)
// - depends on: utf8_decode
// % note: We feel the main purpose of this function should be to ease the transport of data between php & js
// % note: Aiming for PHP-compatibility, we have to translate objects to arrays
// * example 1: unserialize('a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}');
// * returns 1: ['Kevin', 'van', 'Zonneveld']
// * example 2: unserialize('a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}');
// * returns 2: {firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'}
var that = this;
var utf8Overhead = function (chr) {
// http://phpjs.org/functions/unserialize:571#comment_95906
var code = chr.charCodeAt(0);
if (code < 0x0080) {
return 0;
}
if (code < 0x0800) {
return 1;
}
return 2;
};
var error = function (type, msg, filename, line) {
throw new that.window[type](msg, filename, line);
};
var read_until = function (data, offset, stopchr) {
var buf = [];
var chr = data.slice(offset, offset + 1);
var i = 2;
while (chr != stopchr) {
if ((i + offset) > data.length) {
error('Error', 'Invalid');
}
buf.push(chr);
chr = data.slice(offset + (i - 1), offset + i);
i += 1;
}
return [buf.length, buf.join('')];
};
var read_chrs = function (data, offset, length) {
var buf;
buf = [];
for (var i = 0; i < length; i++) {
var chr = data.slice(offset + (i - 1), offset + i);
buf.push(chr);
length -= utf8Overhead(chr);
}
return [buf.length, buf.join('')];
};
var _unserialize = function (data, offset) {
var readdata;
var readData;
var chrs = 0;
var ccount;
var stringlength;
var keyandchrs;
var keys;
if (!offset) {
offset = 0;
}
var dtype = (data.slice(offset, offset + 1)).toLowerCase();
var dataoffset = offset + 2;
var typeconvert = function (x) {
return x;
};
switch (dtype) {
case 'i':
typeconvert = function (x) {
return parseInt(x, 10);
};
readData = read_until(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'b':
typeconvert = function (x) {
return parseInt(x, 10) !== 0;
};
readData = read_until(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'd':
typeconvert = function (x) {
return parseFloat(x);
};
readData = read_until(data, dataoffset, ';');
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 1;
break;
case 'n':
readdata = null;
break;
case 's':
ccount = read_until(data, dataoffset, ':');
chrs = ccount[0];
stringlength = ccount[1];
dataoffset += chrs + 2;
readData = read_chrs(data, dataoffset + 1, parseInt(stringlength, 10));
chrs = readData[0];
readdata = readData[1];
dataoffset += chrs + 2;
if (chrs != parseInt(stringlength, 10) && chrs != readdata.length) {
error('SyntaxError', 'String length mismatch');
}
// Length was calculated on an utf-8 encoded string
// so wait with decoding
readdata = that.utf8_decode(readdata);
break;
case 'a':
readdata = {};
keyandchrs = read_until(data, dataoffset, ':');
chrs = keyandchrs[0];
keys = keyandchrs[1];
dataoffset += chrs + 2;
for (var i = 0; i < parseInt(keys, 10); i++) {
var kprops = _unserialize(data, dataoffset);
var kchrs = kprops[1];
var key = kprops[2];
dataoffset += kchrs;
var vprops = _unserialize(data, dataoffset);
var vchrs = vprops[1];
var value = vprops[2];
dataoffset += vchrs;
readdata[key] = value;
}
dataoffset += 1;
break;
default:
error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype);
break;
}
return [dtype, dataoffset - offset, typeconvert(readdata)];
};
return _unserialize((data + ''), 0)[2];
}
};
try {
$(Mists.ready);
} catch (e) {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment