Created
April 19, 2012 07:06
-
-
Save ql-owo-lp/2419280 to your computer and use it in GitHub Desktop.
Layout+ Script
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
// ==UserScript== | |
// @name Layout+ | |
// @version 0.1 | |
// @id layout_plus_pokerface | |
// @description Google Plus Forger. Make your own Google Plus. | |
// @namespace me.senseofti.toolkit.google | |
// @license GPL v3 or later version | |
// @exclude *://plus.google.com/_/apps-static/* | |
// @exclude *://plus.google.com/u/*/_/* | |
// @exclude *://plus.google.com/up/* | |
// @include *://plus.google.com/* | |
// @author Pokerface - Kevin | |
// ==/UserScript== | |
/* | |
Layout Plus | |
Kevin Wang (kevixw'At'gmail.com) | |
Copyright (c) 2012 . All rights reserved. | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
/* | |
KissogramToolkit | |
build 5/13/2012 by Kevin Wang | |
[email protected] | |
*/ | |
var $K = KissogramToolkit = (function ($$d) { | |
// some configuration | |
var DEBUG_ON = false; | |
// Basic function ============================================== | |
// for each | |
function each($arr, $func) { | |
var item; | |
if (!$arr) | |
return; | |
//_debug('each function is called. arr length is '+ $arr.length); | |
if ($func) | |
for (item in $arr) | |
$func.call($arr[item]); | |
else | |
// the $arr is collection of function itself | |
for (item in $arr) | |
if (typeof $arr[item]==='function') | |
$arr[item](); | |
} | |
var utils = { | |
"isStrictArray" : function ($obj) { | |
return Object.prototype.toString.apply($obj) === '[object Array]'; | |
}, | |
"isRegExp" : function ($obj) { | |
return Object.prototype.toString.apply($obj) === '[object RegExp]'; | |
}, | |
"toArray" : function ($obj) { | |
if (!this.isArray($obj)) | |
return [$obj]; | |
else if (!this.isStrictArray($obj)) | |
return Array.prototype.slice.apply($obj); | |
else | |
return $obj; | |
}, | |
"isArray" : function ($obj) { | |
var type = Object.prototype.toString.apply($obj); | |
return type === '[object Array]' // array | |
|| type === '[object NodeList]' // document.querySelectorAll | |
|| type === '[object Arguments]' // function arguments | |
; | |
}, | |
"trim" : function ($str) { | |
return $str.replace(/^\s+|\s+$/g, ''); | |
}, | |
"trimS" : function ($str) { | |
return this.trim($str).replace(/\s{2,}/g, ' '); | |
} | |
}; | |
// shallow copy | |
function extend($target, $options) { | |
var name; | |
for (name in $options) { | |
if ($target === $options[name]) | |
continue; | |
if ($options[name]) | |
$target[name] = $options[name]; | |
} | |
return $target; | |
} | |
// Basic function ends ============================================= | |
// limit the interval/delay of running a specific function, return the changed function | |
function setFunctionTiming($func, $opt) { | |
$opt = $opt || {}; | |
var opt = { | |
interval : $opt.interval || 0, | |
delay : $opt.delay || 0, | |
check : $opt.check || 0 | |
}; | |
return (function () { | |
var lastRunTime = 0, instances = [], isRunning = false, checkInterval = null; | |
var res = function () { | |
if (opt.check > 0 && checkInterval==null) | |
checkInterval = setInterval(res, opt.check); | |
var timeRemain = (new Date().getTime()) - lastRunTime; | |
var _this = this, args = utils.toArray(arguments); | |
// the real function | |
function runFunc() { | |
lastRunTime = new Date().getTime(); | |
isRunning = true; | |
$func.apply(_this, args); | |
isRunning = false; | |
instances.shift(); | |
}; | |
if (instances.length < 1 || isRunning) { | |
// not time yet | |
if (timeRemain < opt.interval) | |
instances.push(setTimeout(runFunc, Math.max(100, opt.delay + opt.interval - (isRunning ? 0 : timeRemain)))); | |
else | |
instances.push(setTimeout(runFunc, Math.max(100, opt.delay))); | |
} | |
}; | |
return res; | |
})(); | |
} | |
var $$browser = (function getNavigator($n) { | |
var navigatorString = $n.userAgent.toLowerCase(), | |
// browser agent | |
rBrowsers = [ | |
/.*version\/([\w.]+).*(safari).*/, | |
/.*(msie) ([\w.]+).*/, | |
/.*(firefox)\/([\w.]+).*/, | |
/(opera).+version\/([\w.]+)/, | |
/.*(chrome)\/([\w.]+).*/ | |
], | |
// result | |
ret = { | |
name : 'unknown', | |
version : 'unknown', | |
language: $n.language || $n.userLanguage || '', | |
toString : function () { | |
return this.name; | |
} | |
}; | |
for (var i = 0, match=null; i < rBrowsers.length; ++i) | |
if ( match = rBrowsers[i].exec(navigatorString) ) { | |
// match safari | |
ret.name = (i==0 ? match[2] : match[1]) || 'unknown'; | |
ret.version = (i==0 ? match[1] : match[2]) || 'unknown'; | |
ret[ret.name] = true; | |
break; | |
} | |
return ret; | |
})(navigator); | |
// get unsafeWindow | |
var $$w = (function () { | |
var w = null, // window object | |
resizeTasks = [], | |
scrollTasks = []; | |
function getSize() { | |
return { | |
windowHeight : window.innerHeight, | |
windowWidth : window.innerWidth, | |
height : $$d.documentElement.clientHeight, | |
width : $$d.documentElement.clientWidth | |
}; | |
} | |
function _isScroll(el) { | |
// test targets | |
var elems = el ? [el] : [document.documentElement, document.body]; | |
var scrollX = false, scrollY = false; | |
for (var i = 0; i < elems.length; i++) { | |
var o = elems[i]; | |
// test horizontal | |
var sl = o.scrollLeft; | |
o.scrollLeft += (sl > 0) ? -1 : 1; | |
o.scrollLeft !== sl && (scrollX = scrollX || true); | |
o.scrollLeft = sl; | |
// test vertical | |
var st = o.scrollTop; | |
o.scrollTop += (st > 0) ? -1 : 1; | |
o.scrollTop !== st && (scrollY = scrollY || true); | |
o.scrollTop = st; | |
} | |
// ret | |
return { | |
scrollX: scrollX, | |
scrollY: scrollY | |
}; | |
}; | |
window.addEventListener('resize', function () { | |
each(resizeTasks); | |
}, false); | |
window.addEventListener('scroll', function () { | |
each(scrollTasks); | |
}, false); | |
// return true when unsafeWindow is loaded successfully | |
function _init($var) { | |
if (!w) { | |
// load unsafeWindow | |
if (typeof(unsafeWindow) !== "undefined" && typeof(unsafeWindow[$var]) !== "undefined") | |
w = unsafeWindow; | |
else if (typeof(window[$var]) !== "undefined") | |
w = window; | |
else | |
try { | |
// for Chrome | |
var a = document.createElement("a"); | |
a.setAttribute("onclick", "return window;"); | |
var win = a.onclick(); | |
if (typeof(win[$var]) !== "undefined") | |
w = win; | |
} | |
catch (e) { | |
_debug('Kissogram Toolkit : Unable to load unsafeWindow Object!'); | |
w = null; | |
} | |
} | |
return w; | |
} | |
function _onUnsafeWindowReady($var, $func, $options) { | |
$options = $options || {}; | |
$options.retry = (typeof $options.retry != "number") ? 30 : $options.retry; | |
$options.interval = $options.interval || 300; | |
if (_init($var) && (!$options.test || $options.test(w))) { | |
_debug("Kissogram Toolkit : unsafeWindow injection succeed!"); | |
return $func(w, w[$var]); | |
} | |
if ($options.retry-- > 0) | |
setTimeout(function () { _onUnsafeWindowReady($var, $func, $options); }, $options.interval); | |
} | |
var $c = { | |
// get unsafeWindow if possible | |
get : function ($var) { | |
return (this.getUnsafeWindow($var) || window)[$var]; // return safe window | |
}, | |
// get unsafeWindow | |
getUnsafeWindow : function ($var) { | |
return _init($var); | |
}, | |
/* | |
when specific function is ready | |
options : { | |
test : a function that test if specific variable is loaded properly | |
retry : retry times before test() returns a failure, default is 40 | |
interval : the interval between every check, default is 300 ms | |
} | |
*/ | |
onReady : _onUnsafeWindowReady , | |
size : getSize, | |
onResize : function ($func, $init) { | |
if ($init) | |
$func(); | |
resizeTasks.push($func); | |
return resizeTasks.length-1; | |
}, | |
_onResize : function ($id) { | |
if ($id || $id==0) | |
delete resizeTasks[$id]; | |
}, | |
isScroll : _isScroll, | |
onScroll : function ($func, $init) { | |
if ($init) | |
$func(); | |
scrollTasks.push($func); | |
return scrollTasks.length-1; | |
}, | |
_onScroll : function ($id) { | |
if ($id || $id==0) | |
delete scrollTasks[$id]; | |
} | |
}; | |
return $c; | |
})(); | |
var i18n = (function () { | |
var $c = function ($args) { | |
}; | |
var methods = { | |
lang : "en-US", | |
getMessage : function ($item) { | |
} | |
}; | |
})(); | |
// css Class | |
var $$css = (function () { | |
var css_enabled = [], | |
root = $$d.documentElement, | |
FEATURE_LIST_ATTR = "feature-list", | |
CSS_ELEM_REGEXP = /^\s*[.\w][.\w\d-]+[\w\d-]\s*$/; | |
var instance = function ($arg) { | |
extend(this, $c); | |
if ($arg) | |
this.dictionary = this.dictionary.concat($arg.reverse()); //define the css dictionary | |
}; | |
// effective only for Chrome | |
function _getMediaQueriesWidth() { | |
if ($$browser == "firefox") | |
return window.innerWidth; | |
// for Chrome, the width in Media Queries is quite close to window.outerWidth | |
for (var i=1, width = window.outerWidth, match=false; !match; i++) { | |
if (width > 0) | |
match = window.matchMedia('(min-width :' + width + 'px) and (max-width:'+ width + 'px)').matches; | |
if (match) | |
return width; | |
width += (i%2 == 0 ? 1 : -1) * i; | |
} | |
} | |
var $c = { | |
dictionary : [], | |
ns : 'gpp-', // name space | |
// append a class to an element | |
add : function ($elem, $className) { | |
if (!$elem || !CSS_ELEM_REGEXP.test($className = this.get($className))) | |
return; | |
$className = $className.replace(/\./g, ' '); | |
var arr = $className.split(' '), appendList = ""; | |
for (var i=0, clazz = " "+$elem.className+" "; i<arr.length; i++) | |
if (arr[i] && clazz.indexOf(' '+arr[i]+' ') < 0) | |
appendList+= ' '+ arr[i]; | |
$elem.className = utils.trimS($elem.className + appendList); | |
}, | |
remove : function ($elem, $className) { | |
if (!$elem || !CSS_ELEM_REGEXP.test($className = this.get($className))) | |
return; | |
$className = utils.trimS($className.replace(/\./g, ' ')); | |
var arr = $className.split(' '), clazz = " "+$elem.className+" "; | |
for (var i=0; i<arr.length; i++) | |
clazz = clazz.replace(' '+ arr[i] +' ', ' '); | |
$elem.className = utils.trimS(clazz); | |
}, | |
// append css | |
set : function ($str) { | |
GM_addStyle(this.get($str)); | |
}, | |
get : function ($str) { | |
$str = ($str || '').replace(/\/\*[\s\S]*?\*\//g, ''); // clear the comment | |
// backforwards | |
for (var i=0; i<this.dictionary.length; i++) | |
$str = $str.replace(this.dictionary[i][0], this.dictionary[i][1]); | |
return $str; | |
}, | |
push : function ($arg, $str, $opt) { | |
$opt = $opt || {}; | |
var condition = this.getCondition($arg); | |
if ($opt.enable) | |
this.enable($arg, $opt.value); | |
$str = $str.replace(/((?:[^,{]+,?)*)\s*{([^}]+)}/g, condition+"$1 {$2}"); | |
$str = $str.replace(/,/g, ","+condition); | |
this.set($str); | |
}, | |
pull : function ($feature) { | |
return css_enabled[$feature] || null; | |
}, | |
enable : function ($arg) { | |
if (!$arg) return; | |
var feaAttr = this.getFeatureListAttr(), | |
ns = this.ns, | |
data = " "+(root.getAttribute(feaAttr)||"")+" ", | |
appendList = ""; | |
utils.toArray($arg).forEach( | |
function ($item) { | |
var obj = (typeof $item=="string") ? { name: $item } : $item; | |
// when $value is null, assert the it is a boolean | |
if (!obj.value && obj.value != 0) { | |
var name = " "+ obj.name +" "; | |
if (data.indexOf(name) < 0) { | |
appendList += name; | |
} | |
} | |
else | |
root.setAttribute(ns + obj.name, obj.value); | |
css_enabled[obj.name] = obj.value || true; | |
} | |
); | |
if (appendList) | |
root.setAttribute(feaAttr, $K.utils.trimS(data + appendList)); | |
}, | |
disable : function ($arg) { | |
if (!$arg) return ''; | |
var feaAttr = this.getFeatureListAttr(), | |
ns = this.ns, | |
hasFeature = root.hasAttribute(feaAttr), | |
data = " "+(root.getAttribute(feaAttr)||"")+" "; | |
utils.toArray($arg).forEach( | |
function ($item) { | |
var obj = (typeof $item=="string") ? { name: $item } : $item; | |
if (hasFeature) | |
data = data.replace(" "+obj.name+" "," "); | |
root.removeAttribute(ns + obj.name); | |
delete css_enabled[obj.name]; | |
} | |
); | |
if (hasFeature) | |
root.setAttribute(feaAttr, $K.utils.trimS(data)); | |
}, | |
getFeatureListAttr : function () { | |
return this.ns + FEATURE_LIST_ATTR; | |
}, | |
// has specific class | |
is : function ($elem, $className) { | |
if (!$elem) | |
return false; | |
$className = utils.trimS(this.get($className).replace(/\./g, ' ')); | |
var arr = $className.split(' '), clazz = " "+$elem.className+" "; | |
for (var i=0; i<arr.length; i++) | |
if (clazz.indexOf(' '+ arr[i] +' ', ' ') < 0) | |
return false; | |
return true; | |
}, | |
select : function ($str) { | |
return $$d.querySelector(this.get($str)); | |
}, | |
selectAll : function ($str) { | |
return utils.toArray($$d.querySelectorAll(this.get($str))); | |
}, | |
getMediaQueriesWidth : _getMediaQueriesWidth, | |
extendDictionary : function ($dic) { | |
this.dictionary = this.dictionary.concat($dic.reverse()); | |
}, | |
getCondition : function ($arg) { | |
if (!$arg) return ''; | |
var condition = "html", | |
feaAttr = this.getFeatureListAttr(), | |
ns = this.ns; | |
utils.toArray($arg).forEach( | |
function ($item) { | |
var obj = (typeof $item=="string") ? { name: $item } : $item; | |
condition += "["+ (obj.value ? | |
ns + obj.name +'="'+ obj.value +'"' : | |
feaAttr +'~="'+ obj.name +'"')+"]"; | |
} | |
); | |
return condition+' '; | |
}, | |
// return a number of piexl from '##px' | |
getPiexls : function ($str) { | |
if (!/^\d+(px)?$/i.test($str)) | |
return null; // may be 'auto' or anything else | |
return parseInt($str.replace(/px$/i, "")); | |
}, | |
// get the absolute x / y of an element | |
getAbsPos : function ($e) { | |
var t = l = 0; | |
do { | |
t += $e.offsetTop; | |
l += $e.offsetLeft; | |
} while ($e = $e.offsetParent); | |
return { | |
left: l, | |
top: t | |
}; | |
} | |
}; | |
return extend(instance, $c); | |
})(); | |
// manipulate cookies | |
var cookies = (function () { | |
return {}; | |
})(); | |
// dataTransfer | |
var dataTransfer = (function () { | |
var data = {}; | |
return { | |
setData : function ($type, $data) { | |
data[$type] = $data; | |
}, | |
getData : function ($type) { | |
return data[$type]; | |
}, | |
clearData : function ($type) { | |
delete data[$type]; | |
} | |
}; | |
})(); | |
// the Class that process url change | |
var $$url = (function () { | |
var _url = formatUrl(), | |
urlChangeTasks = [], | |
hashChangeTasks = [], | |
urlMonitor = null; | |
function isUrlChanged($url) { | |
var url = formatUrl($url); | |
if (url != _url) { | |
_url = url; | |
return true; | |
} | |
else | |
return false; | |
} | |
// turn http://xxx.xxx into http://xxx.xxx/ | |
function formatUrl($url) { | |
var url = $url || $$d.location.href; | |
if (/^https?:\/\/[\w.]+\w+$/.test(url)) | |
url += '/'; | |
return url; | |
} | |
function execTask($e) { | |
if (!$e) { | |
_debug('Kissogram Toolkit: URL changed!'); | |
each(urlChangeTasks); | |
} | |
else if ($e.type == "popstate") { | |
_debug('Kissogram Toolkit: URL [popstate] changed!'); | |
each(urlChangeTasks); | |
} | |
else if ($e.type == "hashchange") { // hashchange | |
_debug('Kissogram Toolkit: URL [hash] changed!'); | |
each(hashChangeTasks); | |
} | |
} | |
// bind onpopstate | |
window.addEventListener('popstate', function (e) { | |
if (isUrlChanged()) | |
execTask(e); | |
}, false); | |
// hashchange | |
window.addEventListener('hashchange', function (e) { | |
execTask(e); | |
}, false); | |
var $c = { | |
onUrlChange : function ($func, $init) { | |
if ($init) | |
$func(); | |
// mointor | |
if (urlMonitor == null) { | |
_debug('Kissogram Toolkit: URL onChange inited!'); | |
urlMonitor = setInterval(function () { | |
if (isUrlChanged()) | |
execTask(); | |
}, 500); | |
} | |
urlChangeTasks.push($func); | |
}, | |
onHashChange : function ($func, $init) { | |
if ($init) | |
$func(); | |
hashChangeTasks.push($func); | |
}, | |
onUrlMatch : function ($match, $func, $init) { | |
if (!$match) | |
return; | |
}, | |
toString : function () { | |
return _url = formatUrl(); | |
} | |
}; | |
return $c; | |
})(); | |
/* | |
listen to specific event | |
$options { | |
init : boolean / function | |
runOnce : boolean | |
interval | |
} | |
*/ | |
var listen = (function () { | |
var interval_count=[]; // collection of interval count | |
return function ($selector, $event, $func, $options) { | |
$options = $options || {}; | |
// $event & $init cannot be false at the same time | |
if (!$event && !$options.init) | |
return; | |
var evt_listener = (function ($s, $e, $f, $o) { | |
var id = interval_count.length, | |
funcWithTiming = setFunctionTiming($f, { | |
interval : $o.interval || 0, | |
delay : $o.delay || 0 | |
}); | |
// bind event to dom object | |
var _bind = function ($d, $evt) { | |
$d.addEventListener($evt, | |
(function () { | |
var runOnceFunc = setFunctionTiming(function () { | |
$f.apply($d, utils.toArray(arguments), false); | |
$d.removeEventListener($evt, runOnceFunc); | |
}, { delay: $o.delay }), | |
newFunc = function () { | |
funcWithTiming.apply($d, utils.toArray(arguments)); | |
}; | |
return $o.runOnce ? runOnceFunc : newFunc ; | |
})() | |
); | |
}; | |
return function () { | |
// if $s is a element itself | |
var dom = utils.toArray( | |
(typeof $s == 'string') ? $$css.selectAll($s) : $s | |
); | |
if (dom.length > 0) { | |
// dom is captured | |
clearInterval(interval_count[id]); | |
delete interval_count[id]; | |
for (var i=0; i<dom.length; i++) { | |
// if the function need initiation (when the listen function capture the dom objects the first time) | |
if ($o.init) { | |
if (typeof $o.init == "function") | |
$o.init.call(dom[i]); | |
else | |
$f.call(dom[i]); | |
} | |
if (utils.isStrictArray($e)) | |
each($e, function () { _bind(dom[i], this); }); | |
else if ($e) // when $e != null | |
_bind(dom[i], $e); | |
else // do nothing | |
; | |
} | |
} | |
} | |
})($selector, $event, $func, $options); | |
// check it later | |
interval_count.push(setInterval(evt_listener, 500)); | |
} | |
})(); | |
var cache = function () { | |
var _cache = {}; | |
extend(this, { | |
// get a cache | |
get : function ($id) { | |
return _cache[$id]; | |
}, | |
// set a cache | |
set : function ($id, $val) { | |
_cache[$id] = $val; | |
}, | |
// delete a cache | |
"delete" : function ($id) { | |
delete _cache[$id]; | |
}, | |
// clear all cache | |
reset : function () { | |
_cache = {}; | |
}, | |
// if a cache exists | |
exists : function ($id) { | |
return _cache[$id] || _cache[$id]==0; | |
} | |
}); | |
} | |
var mouse = (function () { | |
// simluate a click event | |
function click($elem, $options) { | |
if (!$elem) | |
return; | |
$options = $options || {}; | |
var opt = { | |
button : $options.button || 0 | |
}; | |
// dispatch click event following the W3C order | |
var e = $$d.createEvent("MouseEvents"); | |
e.initMouseEvent("mousedown", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, opt.button, null); | |
$elem.dispatchEvent(e); | |
e = $$d.createEvent("MouseEvents"); | |
e.initMouseEvent("mouseup", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, opt.button, null); | |
$elem.dispatchEvent(e); | |
e = $$d.createEvent("MouseEvents"); | |
e.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, opt.button, null); | |
$elem.dispatchEvent(e); | |
} | |
// gesture part | |
var gesture = (function () { | |
var _zigzag = (function () { | |
var zigzag_finished = 0, | |
event_inited = false, | |
lastX = lastY = X = Y = 0, | |
boundary = 20, // how many pixels should be past when defining a 'zigzag' | |
$c = {}; | |
var _detect = (function () { | |
var directionX = directionY = 0; // record which direction does the mouse move | |
return function (e) { | |
var offsetX = e.clientX - X, offsetY = e.clientY - Y; // direction | |
X = e.clientX; | |
Y = e.clientY; | |
if (!directionX || !directionY) { // init | |
directionX = offsetX; | |
directionY = offsetY; | |
return; | |
} | |
_debug(offsetX * directionX > 0,offsetY * directionY > 0 ,Math.abs(X-lastX) < boundary ,Math.abs(Y-lastY) < boundary); | |
// test if a 'z' is drawed | |
if (offsetX * directionX > 0 || offsetY * directionY > 0 || Math.abs(X-lastX) < boundary || Math.abs(Y-lastY) < boundary) | |
return; | |
lastX = X; | |
lastY = Y; | |
// when three zigzags are achieved | |
if (++zigzag_finished >= 3) | |
$c.success = true; | |
}; | |
})(); | |
function _init(e) { | |
zigzag_finished = 0; | |
X = lastX = e.clientX; | |
Y = lastY = e.clientY; | |
$c.success = false; | |
} | |
$c = { | |
init : _init, | |
detect : _detect, | |
success : false | |
}; | |
return $c; | |
})(); | |
return { | |
"zigzag" : _zigzag | |
}; | |
})(); | |
return { | |
"click" : click, | |
"gesture" : gesture | |
}; | |
})(); | |
function _debug($msg) { | |
if (DEBUG_ON) | |
$K.debug($msg); | |
} | |
// Main function begin ======================================== | |
// constructor | |
var $$c = function () { | |
}; | |
return extend($$c, { | |
"each" : each, | |
"extend" : extend, | |
"css" : $$css, | |
"listen" : listen, | |
"url" : $$url, | |
"dataTransfer" : dataTransfer, | |
"mouse" : mouse, | |
"browser" : $$browser, | |
"window" : $$w, | |
"cookies" : cookies, | |
"select" : function (e) { return $$css.select(e) }, | |
"selectAll" : function (e) { return $$css.selectAll(e) }, | |
"tickTork" : setFunctionTiming, | |
"utils" : utils, | |
"cache" : cache, | |
"i18n" : i18n, | |
"debug" : _debug | |
}); | |
})(document); | |
var GooglePlusPlus = (function ($$d) { | |
$K.debug('Google Plus Plus is now loaded!'); | |
var CSS_DICTIONARY = [ | |
[/<postLocked>/g, '.postLocked'], | |
[/<postFolded>/g, '.postFolded'], | |
[/<postShorten>/g, '.postShorten'], | |
[/<postDragging>/g, '.postOnDragging'], | |
[/<pinColAttr>/g, 'pinter-col'], | |
[/<newCommentArrived>/g, '.newCommentArrived'], | |
[/<mainContent>/g, '.Nj.mu'], | |
[/<streamMainContent>/g, '<mainContent>.a-f-e'], | |
[/<streamContent>/g, 'div[guidedhelpid="streamcontent"]'], | |
[/<streamContainer>/g, '<streamContent> .ow'], | |
[/<anyPost>/g, '.Tg.Sb'], // match the post in the single post page | |
[/<post>/g, '<streamContainer> <anyPost>'], // match the post in the stream | |
[/<invisibleMark>/g, '[style*="display: none;"]'], // not deleted posts | |
[/<postSelected>/g, '.sb'], // post selected mark | |
[/<leftSideNavigation>/g, '.LPb.Zaa'], | |
[/<chatBox>/g, '.Hoa.n0b'], // maxmized chatbox | |
[/<maxmizedChatBox>/g, '<chatBox>.RWa'], // chatbox | |
[/<trendingBox>/g, '.hxa.a-f-e'], | |
[/<postMainContent>/g, '.ii'], | |
[/<shareBoxContainer>/g, '.Cla'], | |
[/<shareBox>/g, '<shareBoxContainer> div[guidedhelpid="sharebox"]'], | |
[/<shareBoxLeftArrow>/g, '<shareBoxContainer> .ze.Dla'], | |
[/<postCommentPanel>/g, '.Lj.FE'], | |
[/<postBody>/g, '<postMainContent> > .ci.gv'], | |
[/<nothingSharedYet>/g, '<streamContainer> > .Ki'], // xxx hasn't share anything with you or no any post | |
[/<filterByCircleOptions>/g, '.kI'], | |
[/<contentPane>/g, '#contentPane'], | |
[/<blockLeftArrow>/g, '.ie'], | |
[/<greyContentBorderAreaLeft>/g, '.vna.KTa'], // the first child of #content | |
[/<greyContentBorderAreaRight>/g, '.vna.K9'], // this appears when the chatbox is maxmized | |
[/<contentBorderArea>/g, '.wna.M5a'], | |
[/<postHeadArea>/g, '.MI'], // the header of a post | |
[/<postTimeAndCircle>/g, '<postHeadArea> .Wp.mc'], | |
[/<postTime>/g, '<postTimeAndCircle> .Ri.lu'], | |
[/<streamMoreButtonContainer>/g, '.ZI.rK.Sca'], | |
[/<streamMoreButton>/g, '<streamMoreButtonContainer> span[role="button"]'], | |
[/<postInnerMedia>/g, '.dv.Mm'], // media including vidoes, photo etc. | |
[/<postInnerMediaContent>/g, '.Yj.Fg'], // media content | |
[/<postInnerVideo>/g,'<postInnerMedia> > iframe'], | |
[/<postMediaBackground>/g, '<postInnerMedia> .Fg'], | |
[/<postInnerImageThumbnail>/g, '.yqrXXd.cPNrJ'], | |
[/<postInnerImageWebPageScreenshot>/g, '.ZF'], // the class of web page screenshot thumbnails | |
[/<postInnerImage>/g, '<postInnerMedia>.Zf > <postInnerMediaContent>:not(<postInnerImageWebPageScreenshot>) img.ev.aG'], | |
[/<postHotonGooglePlusBanner>/g, '.hE.ZX'], | |
[/<postInnerImageContainer>/g, '<postInnerMedia> > <postInnerMediaContent>:not(<postInnerImageWebPageScreenshot>)'], | |
[/<videoThumbnail>/g, '<postInnerMedia>:not(.Zf) > <postInnerMediaContent> img.ev.aG'], | |
[/<videoIntroduction>/g, '<postInnerMedia>:not(.Zf) .ru'], | |
[/<singlePostProfileBlock>/g, '.kM5Oeb-OKUawf.hsHREd'], | |
[/<GoogleLogo>/g, '#gbq1'], | |
[/<GoogleBarTopGeryArea>/g, '.Dob'], | |
[/<postComment>/g, '.Gq'], | |
[/<shareThisPostButton>/g, '.Tj.pk'], | |
[/<nullPost>/g, '<post>.null-post'], | |
[/<userPhoto>/g, '.g-s-n-aa.Wk'], | |
[/<settingHelpFeedbackLinks>/g, '.LCa'], | |
[/<searchFilter>/g, '.Rgc .w0b'], | |
[/<matchedPerson>/g, '.Noa.G0b'], | |
[/<profileIntroduction>/g, '.J7.zaSESc'], | |
[/<fromTheWebText>/g, '<post> <postBody> .HL'], | |
[/<profilePage>/g, '.vcard'], | |
[/<profileAction>/g, '.l-ul.IT.Uf'], | |
[/<myGames>/g, '.CLb'], | |
[/<fromTheWebMarkPic>/g, '.l7.aM'], | |
[/<gameStream>/g, '.DLb'], | |
[/<navigationBarBottomLine>/g, '.Dob'], // this control the height of grey area in the top | |
[/<leftNavigationGreyBackground>/g, '.t9a.mdb'], | |
[/<blackNavigationBar>/g, '#gbx3'], | |
[/<postUserName>/g, '.cK'], | |
[/<notEnoughPostsInYourStream>/g, '.ZNa.aS'], | |
[/<postActionButtons>/g, '<postMainContent> > .LI'], | |
[/<webPageIntroduction>/g, '.pg.XF'], | |
[/<evenPost>/g, '<post>:not(<invisibleMark>):nth-last-child(even)'], | |
[/<oddPost>/g, '<post>:not(<invisibleMark>):nth-last-child(odd)'], | |
[/<editProfileBar>/g, '.gDa.rX.mU.Eg.nU'], | |
[/<optionMenu>/g, '.a-q'], // operation menu of a post | |
[/<saveTheSearchButton>/g, '.Tbc.x0b'], | |
[/<postEventImageThumbnail>/g, '.CI5GWc'], | |
[/<filterByCircleOptionsMainBar>/g, '<filterByCircleOptions> .mp.pu'] | |
]; | |
var HIDE_CHATBOX = "hide-chatbox", | |
HIDE_LEFT_SIDE_NAVIGATION = "hide-left-side-navigation", | |
DISPLAY_CIRCLE_INFO = 'display-circle-info', | |
BLACK_NAVIGATION_EXIST = 'black-navigation-exist'; | |
var css = new $K.css(CSS_DICTIONARY); | |
// hide the left side menu | |
css.push(HIDE_LEFT_SIDE_NAVIGATION, | |
"<leftSideNavigation>, <navigationBarBottomLine> { \ | |
transition: left .8s ease; \ | |
-moz-transition: left .8s ease; \ | |
-webkit-transition: left .8s ease; \ | |
} \ | |
/* for search page */ \ | |
<leftSideNavigation> <leftNavigationGreyBackground> { \ | |
display: block !important; \ | |
top: 0px; \ | |
} \ | |
/* hide the triangle pointer on search page */ \ | |
<leftSideNavigation> <leftNavigationGreyBackground><invisibleMark> .fEb { \ | |
border-bottom: 0; \ | |
} \ | |
<leftSideNavigation> <leftNavigationGreyBackground><invisibleMark> .eEb { \ | |
border-right: 1px solid #CCC; \ | |
} \ | |
/* triangle pointer */ \ | |
<leftSideNavigation> <leftNavigationGreyBackground><invisibleMark> .NDb { \ | |
border-top: 0; \ | |
} \ | |
/* search page end */ \ | |
<leftSideNavigation>:not(:hover) { left: -90px; } \ | |
<greyContentBorderAreaLeft>, <contentBorderArea> { margin-left: 10px; } \ | |
/* bottom line of the white navigation bar */ \ | |
<leftSideNavigation>.gEb:not(:hover) ~ <contentBorderArea> <navigationBarBottomLine> { \ | |
left : 11px; \ | |
} \ | |
<leftSideNavigation>:not(:hover) .NWShHf.pQV9re { \ | |
margin-left: 90px; \ | |
} \ | |
<leftSideNavigation> ~ <contentBorderArea> .uBkbrd.D0jntb:not(.EvUqIc) { left : 0px } \ | |
<leftSideNavigation>:hover ~ <contentBorderArea> .uBkbrd.D0jntb:not(.EvUqIc) { display: none; } \ | |
/* fix left side navigation' border */ \ | |
<leftSideNavigation> .r7omcb { \ | |
top:-29px; \ | |
border-left: 100px solid #F1F1F1; \ | |
border-right: 1px solid #D1D1D1; \ | |
height: 100% !important; \ | |
} \ | |
", { enable : true } | |
); | |
// | |
// hide the chatbox | |
css.push(HIDE_CHATBOX, | |
'<maxmizedChatBox> { \ | |
transition: right .8s ease; \ | |
-moz-transition: right .8s ease; \ | |
-webkit-transition: right .8s ease; \ | |
z-index: 986; \ | |
} \ | |
<maxmizedChatBox>:not(:hover) { right : -160px; } \ | |
<greyContentBorderAreaRight>, <contentBorderArea> { margin-right: 65px; } \ | |
<navigationBarBottomLine> { right : 66px }\ | |
', { enable : true } | |
); | |
// hide the white navigation bar | |
css.push(BLACK_NAVIGATION_EXIST, | |
" \ | |
/* move up the user photo */ \ | |
#gb #gbw #gbu { z-index: 998; top: -19px; } \ | |
#gb #gbb { top: -49px; } \ | |
#gb.gbes #gbb { top: -35px; } \ | |
/* user name */ \ | |
.gbgt#gbg6 #gbi4t { color:white; } \ | |
/* user profile photo */ \ | |
#gb:not(.gbes) #gbw #gbu { top: -19px; } \ | |
#gb:not(.gbes) #gbg4 { margin-top: -1px; } \ | |
#gb:not(.gbes) #gbg4 #gbgs4, #gb:not(.gbes) #gbg4 #gbgs4 > img { \ | |
height: 30px; \ | |
width: 30px; \ | |
} \ | |
#gb.gbes #gbu .gbtc { margin-top: 10px; } \ | |
#gb.gbes #gbu .gbmai { display: none; } \ | |
#gb:not(.gbes) #gbu { margin-right: 0; } \ | |
/* */ \ | |
#content { margin-top: 16px; } \ | |
#gb { width: 100%; position: fixed; } \ | |
/* animation for white bar height change */ \ | |
#gb, #gbx1 { \ | |
transition: height .8s ease .4s; \ | |
-moz-transition: height .8s ease .4s; \ | |
-webkit-transition: height .8s ease .4s; \ | |
} \ | |
/* transparent black */ \ | |
<blackNavigationBar> { opacity: 0.8 } \ | |
/* hide the white navigation bar */ \ | |
#gb:not(:hover) #gbw #gbq { \ | |
top: -80px; \ | |
} \ | |
/* white bar part */ \ | |
#gb:not(:hover) #gbx1 { height: 0px; } \ | |
/* the grey area of the top bar */ \ | |
<GoogleBarTopGeryArea> { max-height: 12px; } \ | |
#gb #gbx1 { border-bottom: 1px solid #ccc; } \ | |
/* navigation height */ \ | |
#gb:not(:hover) { height: 30px; } \ | |
div.gbes#gb:not(:hover) { height: 25px; } \ | |
/* fix the height of white bar */ \ | |
div.gbes#gb { height: 62px; } \ | |
/* bottom shadow line of the grey area */ \ | |
.wl.cVa.V9 { display: none; } \ | |
#gbq { \ | |
transition: top .8s ease .4s; \ | |
-moz-transition: top .8s ease .4s; \ | |
-webkit-transition: top .8s ease .4s; \ | |
} \ | |
<maxmizedChatBox> { margin-top: -50px; } \ | |
<contentPane> { margin-top: 0px; } \ | |
<editProfileBar> { top: 61px; } \ | |
<navigationBarBottomLine> ~ <contentPane> <editProfileBar> { top: 66px; } \ | |
" | |
); | |
// new style that enable auto hiding the left side menu | |
css.set( | |
' \ | |
<filterByCircleOptionsMainBar> { \ | |
border-bottom : 0px; \ | |
position: fixed; \ | |
background-color: rgba(255, 255, 255, 0.75) ; \ | |
} \ | |
/* fix the indent */ \ | |
<filterByCircleOptions> > div.Lg { \ | |
position: relative; \ | |
} \ | |
/* save this search */ \ | |
<saveTheSearchButton> { float: right; } \ | |
' | |
); | |
// basic css for all style | |
css.set( | |
'<streamContainer> { padding: 20px; }'+ | |
'<contentPane> { height: 100%; } '+ // main content | |
'<postCommentPanel> { z-index: 1; }'+ // make the comment panel cover the pictures | |
'<postMediaBackground> { background: transparent; }' + | |
'<shareThisPostButton> + a[class="Tj"][style]:not([role]) ~ div:not([class])[style~="z-index:"][style^="box-shadow:"] { margin-top: -120px }'+ // hack for the plughin 'reply and more' | |
'<profileIntroduction>:not(:hover) { \ | |
max-height: 85px; \ | |
overflow-y: hidden; \ | |
}'+ // live where - profile page | |
'<profilePage> <profileAction> { margin-left:-250px; position: absolute; top: 220px; }'+ // 'block someone' button on profile page | |
'<profilePage> <settingHelpFeedbackLinks> { display: none; }'+ // settings, help, feedback etc. | |
'<profilePage> div[role="tabpanel"][id$="-about-page"] { margin-left: -30px; }'+ // about section | |
'<profilePage> div[role="tabpanel"][id$="-about-page"], \ | |
<profilePage> div[role="tabpanel"][id$="-plusones-page"], \ | |
<profilePage> div[role="tabpanel"][id$="-posts-page"] { \ | |
margin-top: 100px; \ | |
} \ | |
<profilePage> div[role="tabpanel"][id$="-photos-page"], \ | |
<profilePage> div[role="tabpanel"][id$="-videos-page"] { \ | |
margin-top: 80px; \ | |
} \ | |
/* hide the left arrow of share box */ \ | |
<shareBoxLeftArrow> { display: none; } \ | |
<postMainContent> <webPageIntroduction> { max-height : 150px }'+ // max-height of the text introduction of a web page | |
'<streamMoreButtonContainer> { margin-top: 400px; }' // initial top of more botton | |
); | |
// deal with the trending box | |
css.set('<trendingBox> { display:none }'); | |
css.push(DISPLAY_CIRCLE_INFO, | |
' \ | |
/* "xxx" have you in circle / trending box */ \ | |
<mainContent> <profilePage> <trendingBox> { \ | |
display: inline-block; \ | |
z-index: 10; \ | |
background-color: white; \ | |
position: relative; \ | |
float: right; \ | |
width: 290px; \ | |
} \ | |
<mainContent> <profilePage> <trendingBox> { \ | |
border: 1px solid #ccc; \ | |
/* original width is 260px */ \ | |
width: 260px; \ | |
} \ | |
' | |
); | |
var isNewPostArrived = (function () { | |
var firstPostId = null; | |
return function () { | |
var firstPost = css.select('<post>:first-child'); | |
if (!firstPost) | |
return false; | |
var curFirstPostId = firstPost.getAttribute('id'); | |
if (firstPostId != curFirstPostId) | |
return !!(firstPostId = curFirstPostId); | |
else | |
return false; | |
}; | |
})(); | |
/* | |
Timeline Style | |
*/ | |
var GTimeline = (function () { | |
function _init_CSS() { | |
css.set( | |
'<post> { \ | |
display: inline-block; \ | |
padding: 5px; \ | |
margin: -10px 0 20px 0; \ | |
} \ | |
<evenPost> <postMainContent> > <userPhoto>, \ | |
<evenPost> <postMainContent> > .nINTy {\ | |
position: absolute; \ | |
display: block; \ | |
margin-left: 573px; \ | |
} \ | |
<evenPost> <blockLeftArrow> { \ | |
background: no-repeat url(//ssl.gstatic.com/s2/oz/images/sprites/stream-a577cb9e1965785ee8ad2e71f3e8cc5e.png) 0px -21px; \ | |
margin-left: 495px; \ | |
} \ | |
<evenPost> <postHeadArea> { \ | |
text-align: right; \ | |
margin-right: 25px; \ | |
} \ | |
<evenPost> <postUserName> { \ | |
margin-left: 10px; \ | |
float: right; \ | |
} \ | |
<oddPost> { \ | |
clear: both; \ | |
float: right; \ | |
} \ | |
<streamContainer> { \ | |
margin: 0 auto; \ | |
padding:20px; \ | |
margin-bottom:10px; \ | |
width:1150px; \ | |
background: url("data:image/png;base64,'+ | |
'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'+ | |
'") top center repeat-y; \ | |
} \ | |
' | |
); | |
} | |
var checkLayout = (function () { | |
css.set('<nullPost> { display: none; }'); | |
var lastPostId = null; | |
return function () { | |
var stream = css.select('<streamContainer>'), | |
lastPost = css.select('<post>:not(<invisibleMark>)[id]:last-of-type'), | |
currentLastPostId = null; | |
if (lastPost) | |
currentLastPostId = lastPost.getAttribute('id'); | |
if (!lastPostId || !lastPost) { // first-time run | |
lastPostId = currentLastPostId; | |
return; | |
} | |
else if (currentLastPostId == lastPostId) | |
return; | |
if (css.selectAll('#'+ lastPostId +' ~ <post>:not(<invisibleMark>)[id]').length % 2 != 0) { | |
var nullPost = css.select('<nullPost>'); | |
if (nullPost) | |
stream.removeChild(nullPost); | |
else { | |
var div = $$d.createElement('div'); | |
css.add('<nullPost>'); | |
stream.appendChild(div); | |
} | |
} | |
lastPostId = currentLastPostId; | |
} | |
})(); | |
function _init() { | |
$K.debug("Layout+ Timeline is now loaded."); | |
_init_CSS(); | |
$K.url.onUrlChange( | |
function () { | |
$K.listen(css.get('<streamContainer>'), ['DOMNodeInserted', 'DOMNodeRemoved'], checkLayout); | |
}, true | |
); | |
} | |
return { | |
"init" : _init | |
}; | |
})(); | |
/* | |
Pinterest Style | |
*/ | |
var GPinterest = (function () { | |
var COLUMN_ATTR = css.get('<pinColAttr>'), // attribute name set to each post | |
paddingV = paddingH = 10, // the padding between each block | |
leftNavigationWidth = 100, // left navigation's width is 100px | |
hiddenNavigationWidth = 10, // the width after hiding | |
hiddenChatBoxWidth = 50, // the pixels on the sceen when the chat box is hidden | |
mainContentLeftMargin = 40 + hiddenNavigationWidth, // the margin-left of main content | |
chatBoxWidth = 211, // chat box's width is 211px | |
chatBoxGreyBoard = 20, // grey board (half) | |
chatBoxBorderLine = 1, // the border line of grey board | |
postBlockWidth = 496, // default bricks' width | |
perferColumn = 2, // at least how many columns should be put into the container | |
shortestPostHeight = 199, | |
deletedPostHeight = 38, | |
reCalculateLayoutEachXPixels = 5, | |
currentColumn = 0, | |
maxContainerWidth = getMaxContainerWidth(window.innerWidth), | |
brickParameters = [], | |
$$data = _init_posts_data(), | |
LAYOUT_OPERATION_UPDATE_COLUMN = 'update-column'; | |
// init posts' data | |
function _init_posts_data() { | |
return { | |
"posts" : {}, | |
"index" : { | |
"locked" : {}, | |
"folded" : {} | |
}, | |
"other" : {}, | |
"cache" : { | |
"elem" : new $K.cache("elem") | |
} | |
}; | |
} | |
function _init_CSS() { | |
// animation of the post when a new comment is arrived | |
css.set( | |
"<post><newCommentArrived> <postMainContent> > <userPhoto> img { \ | |
animation: rotateUserPhoto 5.5s ease-in-out 1s infinite normal; \ | |
-moz-animation: rotateUserPhoto 5.5s ease-in-out 1s infinite normal; \ | |
-webkit-animation: rotateUserPhoto 5.5s ease-in-out 1s infinite normal; \ | |
} \ | |
@keyframes rotateUserPhoto { \ | |
0% { transform: rotate(360deg); } \ | |
20% { transform: rotate(0deg); } \ | |
} \ | |
@-moz-keyframes rotateUserPhoto { \ | |
0% { -moz-transform: rotate(360deg); } \ | |
20% { -moz-transform: rotate(0deg); } \ | |
} \ | |
@-webkit-keyframes rotateUserPhoto { \ | |
0% { -webkit-transform: rotate(360deg); } \ | |
20% { -webkit-transform: rotate(0deg); } \ | |
} \ | |
<post><newCommentArrived> { \ | |
transition:border linear .2s,box-shadow linear .5s; \ | |
-moz-transition:border linear .2s,-moz-box-shadow linear .5s; \ | |
-webkit-transition:border linear .2s,-webkit-box-shadow linear .5s; \ | |
outline:none; \ | |
border-color: rgba(0, 97, 255, 0.75); \ | |
box-shadow: 0 0 30px rgba(0, 97, 255, 0.95); \ | |
-moz-box-shadow: 0 0 30px rgba(0, 97, 255, 0.95); \ | |
-webkit-box-shadow: 0 0 30px rgba(0, 97, 255,0.95); \ | |
} \ | |
" | |
); | |
css.set( | |
"/* center the main stream, except on the page 'photo from your phone' */ \ | |
<mainContent>:not(.hca) { margin-left : "+ mainContentLeftMargin +"px; } \ | |
/* fix the 'hot on google's border */ \ | |
<postHotonGooglePlusBanner> { \ | |
margin-top: -3px; \ | |
margin-left: 0px; \ | |
} \ | |
/* fix the comment's border */ \ | |
<post> <postCommentPanel> { border-top: 0px; top: 0px; margin: 0; } \ | |
/* put the user photo into post start */ \ | |
<post> <postHeadArea>, <post> <fromTheWebMarkPic> { \ | |
margin-left: 50px; \ | |
margin-top: 10px; \ | |
min-height: 40px; \ | |
} \ | |
/* on search page, 'from the web' */ \ | |
<post> <postMainContent> > <fromTheWebMarkPic> { margin-left: 15px; } \ | |
/* from the web text */ \ | |
<fromTheWebText> { \ | |
margin-top: -55px; \ | |
margin-left: 60px; \ | |
position: absolute; \ | |
} \ | |
/* end */ \ | |
<post> <postMainContent> > <userPhoto> { \ | |
display: inline; \ | |
float: left; \ | |
margin-left: 80px; \ | |
margin-top: 10px; \ | |
} \ | |
/* fix the event image */ \ | |
<postEventImageThumbnail> { \ | |
max-width: 498px; \ | |
} \ | |
/* put the user photo into post end */ \ | |
<post> <videoThumbnail> { margin: auto; } \ | |
/* fix the position of user's photo on profile page */ \ | |
<mainContent> <singlePostProfileBlock> { margin-top: 0px } \ | |
<blockLeftArrow> { display: none !important; } \ | |
" | |
); | |
// hide the post not ready yet | |
css.set( | |
'<post> { left: -999px; } \ | |
<post>[<pinColAttr>] { left: 0px; } \ | |
' | |
); | |
// post animation | |
css.set( | |
'<post> { \ | |
position: absolute; \ | |
transition: all 1.2s ease-in-out; \ | |
-moz-transition: all 1.2s ease-in-out; \ | |
-webkit-transition: all 1.2s ease-in-out; \ | |
} \ | |
' | |
); | |
// animation for selected post | |
css.set( | |
'<post><postSelected> { \ | |
z-index: 2; \ | |
transition:border .2s linear 1.8s, box-shadow .5s linear 2s; \ | |
-moz-transition:border .2s linear 1.8s, -moz-box-shadow .5s linear 2s; \ | |
-webkit-transition:border .2s linear 1.8s, -webkit-box-shadow .5s linear 2s; \ | |
outline:none; \ | |
border-color: rgba(255, 49, 0, 0.75); \ | |
-webkit-box-shadow: 3px 2px 8px rgba(50, 50, 50, 0.45); \ | |
-moz-box-shadow: 3px 2px 8px rgba(50, 50, 50, 0.45); \ | |
box-shadow: 3px 2px 8px rgba(50, 50, 50, 0.45); \ | |
}' | |
); | |
// make the share box act like a post | |
css.set( | |
'/* hide the photo beside the sharebox */ \ | |
<streamMainContent> <streamContent> { \ | |
margin-top: -20px; \ | |
} \ | |
<streamContent> { \ | |
margin-left:-120px; \ | |
} \ | |
/* fix the indent of sharebox on profile page */ \ | |
<profilePage> <shareBoxContainer> { \ | |
position: absolute; \ | |
margin-top: 20px; \ | |
z-index: 4; \ | |
} \ | |
<streamMainContent> <shareBoxContainer>, <searchFilter>, <matchedPerson>, \ | |
<notEnoughPostsInYourStream>, <myGames>, <gameStream> { \ | |
margin-left: -52px; \ | |
position: absolute; \ | |
margin-top: 20px; \ | |
z-index: 4; \ | |
transition: all 1.2s ease-in-out; \ | |
-moz-transition: all 1.2s ease-in-out; \ | |
-webkit-transition: all 1.2s ease-in-out; \ | |
} \ | |
<notEnoughPostsInYourStream> { \ | |
background-color: white; \ | |
} \ | |
/* the post content */ \ | |
<shareBox> .g-Ac[role="button"] { \ | |
width: 100%; \ | |
} \ | |
/* user photo */ \ | |
<shareBoxContainer> <userPhoto> { \ | |
display: none; \ | |
} \ | |
<nothingSharedYet> { \ | |
margin-top: 100px; \ | |
margin-left: '+ (mainContentLeftMargin+50) +'px; \ | |
} \ | |
' | |
); | |
// drag & drop | |
css.set( | |
"<postHeadArea> { cursor: url(//ssl.gstatic.com/s2/oz/images/sge/openhand_8_8.cur),move; } \ | |
<post><postSelected><postLocked> { \ | |
z-index: 6; \ | |
} \ | |
<post><postLocked> { \ | |
position: fixed; \ | |
z-index: 5; \ | |
} \ | |
/* transparent when draging */ \ | |
<post><postDragging> { \ | |
opacity: 0.5; \ | |
} \ | |
/* when it's locked and folded */ \ | |
<post><postFolded><postLocked>, \ | |
<post><postFolded><postLocked> > div, \ | |
<post><postShorten>, <post><postShorten> > div { \ | |
max-width: 280px; \ | |
} \ | |
/* user name */ \ | |
<post><postFolded><postLocked> <postUserName>, \ | |
<post><postShorten> <postUserName> { \ | |
display: block; \ | |
font-family: Georgia; \ | |
} \ | |
<post><postFolded><postLocked> <postHeadArea> header, \ | |
<post><postShorten> <postHeadArea> header { \ | |
margin-top: -10px; \ | |
} \ | |
/* indent of time */ \ | |
<post><postFolded><postLocked> <postTimeAndCircle>, \ | |
<post><postShorten> <postTimeAndCircle> { \ | |
margin-left: 0px; \ | |
} \ | |
/* min-height of a post when it is folded */ \ | |
<post><postFolded> <postMainContent> { \ | |
min-height: 70px; \ | |
} \ | |
<post><postFolded> <postBody>, \ | |
<post><postFolded> <postActionButtons>, \ | |
<post><postFolded> <postHotonGooglePlusBanner>, \ | |
<post><postFolded> <postCommentPanel> { \ | |
display: none; \ | |
} \ | |
<post><postFolded>:hover <postBody> { \ | |
animation: peakAtPostContent .8s ease-in-out; \ | |
-moz-animation: peakAtPostContent .8s ease-in-out; \ | |
-webkit-animation: peakAtPostContent .8s ease-in-out; \ | |
position: absolute; \ | |
margin-top: 20px; \ | |
opacity: 1; \ | |
display:block; \ | |
top: 55px; \ | |
z-index: 8; \ | |
background: white; \ | |
border: 1px solid #ccc; \ | |
border-radius: 2px; \ | |
border-color: rgba(0, 97, 255, 0.75); \ | |
box-shadow: 0 0 30px rgba(0, 97, 255, 0.95); \ | |
-moz-box-shadow: 0 0 30px rgba(0, 97, 255, 0.95); \ | |
-webkit-box-shadow: 0 0 30px rgba(0, 97, 255, 0.95); \ | |
} \ | |
@keyframes peakAtPostContent { \ | |
0% { opacity: 0; } \ | |
80% { opacity: 0; } \ | |
} \ | |
@-moz-keyframes peakAtPostContent { \ | |
0% { opacity: 0; } \ | |
80% { opacity: 0; } \ | |
} \ | |
@-webkit-keyframes peakAtPostContent { \ | |
0% { opacity: 0; } \ | |
80% { opacity: 0; } \ | |
} \ | |
" | |
); | |
} | |
function loadBrickCSS($windowWidth) { | |
var mediaQueriesEnable = false; // enable media queries can accelerate the rending | |
// more flexible when the media queries is off | |
if ($K.browser == "chrome") | |
mediaQueriesEnable = true; | |
var index = Math.floor((mediaQueriesEnable ? $K.css.getMediaQueriesWidth() : window.innerWidth) / reCalculateLayoutEachXPixels), | |
BRICK_PARA_ATTR = 'brick-para-index'; | |
if (!mediaQueriesEnable) | |
css.enable(BRICK_PARA_ATTR, index); | |
if (brickParameters[index]) | |
return brickParameters[index]; | |
var maxContainerWidth = getMaxContainerWidth($windowWidth), | |
para = brickParameters[index] = getBrickParameters(maxContainerWidth), css_string =''; | |
// squeeze user name | |
var uname_style = '<post> <postUserName> { display: block; } \ | |
<post> <postHeadArea> header { margin-top: -10px; } \ | |
<post> <postTime> { margin-left: 0px; }'; | |
// for Chrome | |
if (mediaQueriesEnable) { | |
css_string += '@media (min-width:'+ | |
(index * reCalculateLayoutEachXPixels) + | |
'px) and (max-width:'+ | |
((index+1) * reCalculateLayoutEachXPixels -1) +'px) {'; | |
//$K.debug('Current window width is '+ $windowWidth +'. Apply css Media Queries : '+ css_string); | |
} | |
if ($windowWidth < 1750) | |
css_string += '<filterByCircleOptionsMainBar> { width:'+ (maxContainerWidth - hiddenNavigationWidth - chatBoxBorderLine)+'px; }'; | |
// when screen is too small, compress the user name block. here window's width is only an approximate value | |
if ($windowWidth < 1280) | |
css_string += uname_style; | |
var _offsetLeft = Math.floor((para.brickWidth - postBlockWidth)/2); | |
// set brick width | |
css_string += | |
'<shareBoxContainer>, <shareBox>, <notEnoughPostsInYourStream>, \ | |
<searchFilter>, <matchedPerson>, \ | |
<post>, <post> > div:not(<optionMenu>) { \ | |
width:' + para.brickWidth +'px; \ | |
} \ | |
<postInnerVideo>, <postInnerImageContainer>, \ | |
<videoThumbnail>, <myGames>, <myGames> .KNb, \ | |
<gameStream> { \ | |
width:' + para.brickWidth +'px !important; \ | |
} \ | |
<postInnerImageContainer>, <postInnerImage>, <postInnerImageThumbnail> { \ | |
max-width:' + para.brickWidth +'px !important; \ | |
}'; | |
for (var i=0; i< para.brickColumn; i++) | |
css_string += '<post>[<pinColAttr>="'+i+'"] { left: '+ i * (paddingH + para.brickWidth) +'px; }'; | |
if (mediaQueriesEnable) { | |
css_string += '}'; // end of Media Queries | |
css.set(css_string); | |
} | |
else | |
css.push({ name: BRICK_PARA_ATTR, value : index }, css_string, { enable : true }); | |
return para; | |
} | |
// get current max container width | |
function getMaxContainerWidth($windowWidth) { | |
var _chatBoxWidth = css.pull(HIDE_CHATBOX) ? hiddenChatBoxWidth : chatBoxWidth, | |
_chatBoxGreyBoard = chatBoxGreyBoard, | |
_chatBoxBorderLine = chatBoxBorderLine; | |
if (!css.select('<greyContentBorderAreaRight>')) | |
_chatBoxWidth = _chatBoxGreyBoard = _chatBoxBorderLine = 0; | |
return $windowWidth - _chatBoxWidth - _chatBoxGreyBoard - _chatBoxBorderLine - mainContentLeftMargin; | |
} | |
// re-calculate parameters based on container width | |
function getBrickParameters($containerWidth) { | |
var containerWidth = $containerWidth + paddingH, | |
column = localStorage['GPinterest_peferColumn'] || 0; | |
if (!column) { | |
column = Math.round(containerWidth / (postBlockWidth + paddingH)); | |
// at least three columns should be ensured to display | |
column = column > perferColumn ? column : perferColumn; | |
} | |
var postBrickWidth = Math.floor($containerWidth / column - paddingH); | |
return { | |
"brickColumn" : column, | |
"brickWidth" : postBrickWidth | |
}; | |
} | |
// drag and drop ======= | |
// alert when there is new message | |
function monitorNewMessage($post) { | |
var comment = $post.querySelector(css.get('<postCommentPanel> <postComment>')); | |
comment.addEventListener('DOMNodeInserted', newCommentListener, false); | |
$post.addEventListener('click', cancleNewCommentAlert, false); | |
} | |
function _monitorNewMessage($post) { | |
var comment = $post.querySelector(css.get('<postCommentPanel> <postComment>')); | |
comment.removeEventListener('DOMNodeInserted', newCommentListener, false); | |
$post.removeEventListener('click', cancleNewCommentAlert, false); | |
} | |
function newCommentListener() { | |
var post = this.parentElement.parentElement; | |
css.add(post, '<newCommentArrived>'); | |
} | |
function cancleNewCommentAlert() { | |
css.remove(this, '<newCommentArrived>'); | |
} | |
var foldAndLockMouseDispatcher = (function () { | |
var dragX = dragY = 0, lastDraggedPostId = null, droppable = false; | |
// listen to some basic section | |
$K.listen('#content', 'dragover', | |
function (e) { | |
if (e.preventDefault) e.preventDefault(); | |
$K.debug('dragover'); | |
//$K.mouse.gesture.zigzag.detect(e); | |
return false; | |
} | |
); | |
$K.listen('#content', 'dragenter', | |
function (e) { | |
$K.debug('dropenter'); | |
e.dataTransfer.dropEffect = 'move'; | |
return false; | |
} | |
); | |
$K.listen('#content', 'drop', | |
function (e) { | |
if (e.stopPropagation) e.stopPropagation(); | |
$K.debug('drop content'); | |
var elem = e.target; | |
droppable = droppable && !css.is(elem, "uohZhe"); | |
if (!droppable) | |
return; | |
var offsetX = e.clientX - dragX, | |
offsetY = e.clientY - dragY, | |
p = $$data.posts[lastDraggedPostId], | |
style = getComputedStyle(p.post), | |
X = offsetX + css.getPiexls(style.getPropertyValue('left')), | |
Y = offsetY + css.getPiexls(style.getPropertyValue('top')); | |
lockPostPosition(p, { "X": X, "Y": Y }); | |
return false; | |
} | |
); | |
$K.listen(css.get('<leftSideNavigation>'), 'drop', | |
function (e) { | |
if (e.stopPropagation) e.stopPropagation(); | |
if (e.preventDefault) e.preventDefault(); | |
$K.debug('drop'); | |
resetPostPosition(lastDraggedPostId); | |
droppable = false; | |
return false; | |
} | |
); | |
return function (e) { | |
var elem = e.target, post; | |
$K.debug(elem); | |
if (elem.tagName == "HEADER") | |
elem = elem.parentElement; | |
if (css.is(elem, "<postHeadArea>")) | |
post = elem.parentElement.parentElement.parentElement; | |
if (!css.is(post, '<anyPost>')) | |
return; | |
var pid = post.getAttribute('id'), pdata = $$data.posts[pid]; | |
// fold post | |
pdata.fold(!pdata.fold()); | |
delayedLayout(); | |
if (!elem.hasAttribute('draggable')) { | |
elem.setAttribute('draggable', 'true'); | |
// when drag start | |
elem.addEventListener('dragstart', | |
(function ($pid) { | |
return function (e) { | |
var p = $$data.posts[$pid]; | |
dragX = e.clientX; | |
dragY = e.clientY; | |
css.add(p.post, '<postDragging><postShorten>'); | |
lastDraggedPostId = $pid; | |
$K.debug('dragstart'); | |
e.dataTransfer.effectAllowed = 'move'; | |
$K.dataTransfer.setData('movePost', $pid); | |
p.fold(true); | |
// record zigzag | |
//$K.mouse.gesture.zigzag.init(e); | |
return true; | |
}; | |
})(pid) | |
); | |
elem.addEventListener('dragend', | |
(function ($pid) { | |
return function (e) { | |
var p = $$data.posts[$pid]; | |
$K.debug('dragend'); | |
css.remove(p.post, '<postDragging><postShorten>'); | |
// if this is a zigzag gesture | |
if ($K.mouse.gesture.zigzag.success) { | |
if (p.lock() || p.select()) | |
resetPostPosition($pid); | |
droppable = false; | |
} | |
else | |
droppable = true; | |
}; | |
})(pid) | |
); | |
// reset post's position by double click | |
/*elem.addEventListener('dblclick', | |
(function ($p) { | |
return function () { | |
} | |
})(post) | |
, false);*/ | |
} | |
} | |
})(); | |
// drag and drop =========== | |
// lock a post's position | |
function lockPostPosition($p, $pos) { | |
$p.lock(true); | |
$p.pos.left = $pos.X; | |
$p.pos.top = $pos.Y; | |
monitorNewMessage($p.post); | |
$p.post.style.left = $p.pos.left +'px'; | |
$p.post.style.top = $p.pos.top +'px'; | |
delayedLayout(); | |
} | |
// reset posts' position | |
function resetPostPosition($var) { | |
var p; | |
if (!$var) | |
return; | |
else if (typeof $var==='string') | |
p = $$data.posts[$var]; | |
else | |
p = $$data.posts[$var.getAttribute('id')]; | |
p.select(false); | |
css.remove(this.post, '<postSelected>'); | |
// unlock the post | |
p.lock(false); | |
p.post.style.left = ""; | |
if (p.fold()) { | |
setTimeout(function () { | |
// expand post | |
p.fold(false); | |
delayedLayout(); | |
}, 1800); | |
} | |
_monitorNewMessage(p.post); | |
delayedLayout(); | |
} | |
// float locked posts on the screen | |
$K.window.onResize( | |
function () { | |
$K.each($$data.index.locked, | |
function () { | |
var maxOffsetX = window.innerWidth - this.post.offsetWidth * 1.5; | |
if (maxOffsetX - css.getPiexls(this.post.style.left) < 0) | |
this.post.style.left = maxOffsetX +"px"; | |
} | |
); | |
} | |
); | |
// re-layout the bricks | |
function replaceBricks($brickPara, $opt) { | |
var bricks = css.selectAll('<post>'), | |
brickAmount = bricks.length, | |
NO_BOXSHADOW = 'rgba(0, 0, 0, 0)', | |
$opt = $opt || {}, | |
ignoreColumn = ($brickPara.brickColumn != currentColumn), | |
needUpdateColumn = $opt.operation == LAYOUT_OPERATION_UPDATE_COLUMN, | |
tops = [], needUpdate = []; | |
currentColumn = $brickPara.brickColumn; | |
// inset a element into a column | |
function insertBrickInto($id, $elem, $column) { | |
//$K.debug($elem); | |
if (!$elem) | |
return; | |
if (!$$data.other[$id]) | |
$$data.other[$id] = { | |
top : null, | |
column : -1 | |
}; | |
var dat = $$data.other[$id], height = $elem.offsetHeight; | |
if ($column != dat.column) { | |
if (dat.column > -1) | |
needUpdate[dat.column] = true; | |
dat.column = $column; | |
needUpdate[$column] = true; | |
} | |
if (dat.top != tops[$column]) { | |
needUpdate[$column] = true; | |
dat.top = tops[$column]; | |
} | |
if (needUpdate[$column] || $elem.style.top=="") | |
$elem.style.top = tops[$column]+'px'; | |
tops[$column] += height + paddingV; | |
} | |
for (var i=0; i<currentColumn; i++) { | |
tops.push(0); // default top | |
needUpdate.push((needUpdateColumn && i== $opt.value) || ignoreColumn); // if current column need to be updated | |
} | |
// take the sharebox as the first brick | |
insertBrickInto('shareBoxContainer', css.select('<shareBoxContainer>'), 0); | |
insertBrickInto('notEnoughPostsInYourStream', css.select('<notEnoughPostsInYourStream>'), 0); | |
insertBrickInto('matchedPerson', css.select('<matchedPerson>'), 0); | |
insertBrickInto('searchFilter', css.select('<searchFilter>'), 0); | |
insertBrickInto('myGames', css.select('<myGames>'), 0); | |
insertBrickInto('gameStream', css.select('<gameStream>'), 0); | |
for (var i=0, curCol; i<brickAmount;i++) { | |
// deliver post balancely | |
curCol = (Math.max(brickAmount-1, 0)+i) % currentColumn; // current brick's column | |
//curCol = $K.utils.getMinIndex(tops, exceptionColumns); | |
var postId = bricks[i].getAttribute("id"), | |
postHeight = bricks[i].offsetHeight, | |
needReloadPost = !bricks[i].hasAttribute(COLUMN_ATTR); | |
if (postHeight < deletedPostHeight) // post not ready yet | |
continue; | |
if (!$$data.posts[postId] || bricks[i].style.top=="") { // init | |
needUpdate[curCol] = true; | |
$$data.posts[postId] = { | |
id : postId, | |
column : curCol, | |
pos : { | |
top : 0, // relative | |
left : 0 | |
}, | |
lock : function ($act) { | |
if (typeof $act !="boolean") | |
return !!$$data.index.locked[this.id]; | |
if ($act) { | |
css.add(this.post, '<postLocked>'); | |
$$data.index.locked[this.id] = this; | |
} | |
else { | |
css.remove(this.post, '<postLocked>'); | |
delete $$data.index.locked[this.id]; | |
} | |
}, | |
select : (function () { | |
var _selected = false; | |
return function ($act) { | |
if (typeof $act !="boolean") | |
return _selected; | |
_selected = $act; | |
}; | |
})(), | |
fold : function ($act) { | |
if (typeof $act !="boolean") | |
return !!$$data.index.folded[this.id]; | |
if ($act) { | |
css.add(this.post, '<postFolded>'); | |
$$data.index.folded[this.id] = this; | |
} | |
else { | |
css.remove(this.post, '<postFolded>'); | |
delete $$data.index.folded[this.id]; | |
} | |
}, | |
post : bricks[i], | |
toString : function () { | |
return this.id; | |
} | |
}; | |
} | |
else if (!ignoreColumn && !needReloadPost) | |
curCol = $$data.posts[postId].column; | |
else | |
$$data.posts[postId].column = curCol; | |
var p = $$data.posts[postId]; // reference | |
if (css.is(bricks[i], '<postSelected>')) { | |
var shadowValue = getComputedStyle(bricks[i]).getPropertyValue('box-shadow') || NO_BOXSHADOW; | |
p.select(shadowValue.indexOf(NO_BOXSHADOW) < 0); | |
} | |
else | |
p.select(false); | |
if (!p.lock() && !p.select() && p.pos.top != tops[curCol]) { | |
needUpdate[curCol] = true; | |
p.pos.top = tops[curCol]; | |
} | |
// do not move the selected / locked post | |
if (!p.lock() && !p.select()) { | |
if (ignoreColumn || needReloadPost) | |
bricks[i].setAttribute(COLUMN_ATTR, curCol); // update column information | |
if (needUpdate[curCol]) // when the post is selected before, but not selected any more | |
bricks[i].style.top = tops[curCol]+'px'; | |
} | |
if (!p.lock()) | |
tops[curCol] += postHeight + paddingV; | |
} | |
return Math.max.apply(Math, tops); // return total height | |
} | |
function appendBricks($brickPara) { | |
} | |
var layout = (function () { | |
var maxHeight = 0; | |
return function ($opt) { | |
//$K.debug('G++ Pinterest: Re-layout at '+ new Date().getTime()); | |
var brickPara = loadBrickCSS(window.innerWidth), | |
height = replaceBricks(brickPara, $opt), | |
moreButtonContainer = css.selectAll('<streamMoreButtonContainer>'), | |
contentPane = css.select('<contentPane>'), | |
mainContent = css.select('<mainContent>'); | |
// 'more' button | |
if (moreButtonContainer && maxHeight != height) { | |
maxHeight = height; | |
$K.each( | |
moreButtonContainer, | |
function () { | |
this.style.marginTop = height+'px'; | |
} | |
); | |
mainContent.style.minHeight = contentPane.style.minHeight = (height + 200)+'px'; | |
} | |
}; | |
})(); | |
var timingLayout = $K.tickTork(layout, { interval : 2300, check: 5000 }); | |
var delayedLayout = $K.tickTork(layout, { delay: 2000 }); | |
function _init() { | |
$K.debug("Layout+ Pinterest is now loaded."); | |
_init_CSS(); | |
$K.url.onUrlChange( | |
function () { | |
// reload all posts | |
$$data.index.locked={}; | |
$K.listen(css.get('<streamContainer>, <shareBox>'), 'DOMSubtreeModified', timingLayout, { init: true }); | |
$K.listen(css.get('<streamContainer>'), 'click', foldAndLockMouseDispatcher); | |
}, true | |
); | |
$K.window.onResize(timingLayout); | |
} | |
return { | |
"init" : _init | |
}; | |
})(); | |
$K.debug("Layout+ is now loaded. Current URL "+ $K.url); | |
// cancel a bunch of action | |
$K.url.onUrlChange(function () { | |
if (css.select('<blackNavigationBar>')) { | |
$K.listen(css.get('<blackNavigationBar>'), 'mouseover', function () { | |
css.enable(DISPLAY_CIRCLE_INFO); | |
}); | |
css.enable(BLACK_NAVIGATION_EXIST); | |
} | |
else | |
css.disable(BLACK_NAVIGATION_EXIST); | |
}, true ); | |
$K.listen('#content', 'click', function () { | |
css.disable(DISPLAY_CIRCLE_INFO); | |
}); | |
var Theme = GPinterest; | |
//var Theme = GTimeline; | |
Theme.init(); | |
})(document); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment