Created
April 25, 2012 15:56
-
-
Save ql-owo-lp/2490858 to your computer and use it in GitHub Desktop.
Former G++ Script (Reply function and so on)
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 G++ | |
// @id google_plus_plus_pokerface | |
// @namespace me.senseofti.toolkit.google | |
// @description Google Plus Forger. Make your own Google Plus. | |
// @license GPL v3 or later version | |
// @updateURL http://userscripts.org/scripts/source/127787.meta.js | |
// @exclude *://plus.google.com/_/apps-static/* | |
// @exclude *://plus.google.com/u/0/_/notifications/frame?* | |
// @include *://plus.google.com/* | |
// @version 0.1 | |
// @author Pokerface - Kevin | |
// ==/UserScript== | |
/* | |
Google Plus 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 4/18/2012 by Kevin | |
*/ | |
var $K = KissogramToolkit = (function ($$d) { | |
// Basic function ============================================== | |
// for each | |
function each($arr, $func) { | |
if (!$arr) | |
return; | |
//console.debug('each function is called. arr length is '+ $arr.length); | |
if ($func) | |
for (var i=$arr.length-1; i >=0; i--) | |
$func.call($arr[i]); | |
else | |
// the $arr is collection of function itself | |
for (var i=$arr.length-1; (i >=0 && typeof $arr[i]=='function') ; i--) | |
$arr[i](); | |
} | |
var utils = { | |
"isStrictArray" : function ($obj) { | |
return Object.prototype.toString.apply($obj) === '[object Array]'; | |
}, | |
"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 | |
; | |
}, | |
"getMinIndex" : function ($arr, $exception) { | |
var min = 0, maxNum = Math.max.apply(Math, $arr); | |
if ($exception instanceof Array) | |
$exception = $exception.join(','); | |
$exception = ","+$exception+","; | |
for (var i=0;i<$arr.length;i++) | |
if ($arr[i] <= maxNum && $exception.indexOf(','+i+',') < 0) { | |
min = i; | |
maxNum = $arr[i]; | |
} | |
return min; | |
} | |
}; | |
// shallow copy | |
function extend($target, $options) { | |
for (name in $options) | |
$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 | |
}; | |
var _delayedFunc = opt.delay > 0 ? | |
function () { | |
var _this = this, args = Array.prototype.slice.apply(arguments); | |
setTimeout(function () { | |
$func.apply(_this, args); | |
}, opt.delay); | |
} | |
: $func; | |
//if ($opt.check > 0) | |
// setInterval(_delayedFunc, check); | |
return opt.interval<=0 ? _delayedFunc : (function () { | |
var lastRunTime = 0, isRunning = false; | |
return function () { | |
var check = (new Date().getTime()) - lastRunTime; | |
var _this = this, args = Array.prototype.slice.apply(arguments); | |
// the real function | |
function runFunc() { | |
_delayedFunc.apply(_this, args); | |
lastRunTime = new Date().getTime(); | |
isRunning = false; | |
}; | |
if (!isRunning) { | |
isRunning = true; | |
// not time yet | |
if (check < opt.interval) | |
setTimeout(runFunc, opt.interval - check); | |
else | |
runFunc(); | |
} | |
}; | |
})(); | |
} | |
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 = []; // current window size | |
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); | |
// 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) { | |
console.error('Kissogram Toolkit : Unable to load unsafeWindow Object!'); | |
w = null; | |
} | |
} | |
return w; | |
} | |
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 : function ($var, $func, $options) { | |
$options = $options || {}; | |
$options.retry = $options.retry || 40; | |
$options.interval = $options.interval || 300; | |
var _this=this; | |
if (init($var) && (($options.test && $options.test(w)) || !$options.test)) | |
return $func(w); | |
if (--$options.retry > -1) | |
setTimeout(function () { _this.onReady($var, $func, $options); }, $options.interval); | |
}, | |
size : getSize, | |
onResize : function ($func, $init) { | |
if ($init) | |
$func(); | |
resizeTasks.push($func); | |
}, | |
isScroll : _isScroll | |
}; | |
return $c; | |
})(); | |
// css Class | |
var $$css = (function () { | |
var css_enabled = [], | |
root = $$d.documentElement; | |
var instance = function ($arg) { | |
extend(this, $c); | |
if ($arg) | |
this.dictionary = this.dictionary.concat($arg); //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 : [], | |
// append a class to an element | |
append : function ($elem, $className) { | |
if (!$elem) | |
return; | |
$className = this.get($className); | |
var re = new RegExp(" "+$className+" "); | |
if (!re.test(" "+$elem.className+" ")) | |
$elem.className += ' '+ $className; | |
}, | |
remove : function ($elem, $className) { | |
if (!$elem) | |
return; | |
$className = this.get($className); | |
var re = new RegExp(" "+$className+" "); | |
$elem.className = (" "+$elem.className+" ").replace(re, '').replace(/^\s+|\s+$/g,''); | |
}, | |
// append css | |
set : function ($str) { | |
GM_addStyle(this.get($str)); | |
}, | |
get : function ($str) { | |
for (var i=0; i<this.dictionary.length; i++) | |
$str = $str.replace(this.dictionary[i][0], this.dictionary[i][1]); | |
return $str; | |
}, | |
push : function ($cssName, $str, $opt) { | |
$opt = $opt || {}; | |
var attr = 'enable-css-'+ $cssName, | |
condition = "html["+ attr +($opt.value ? '="'+$opt.value+'"' : '')+"]"; | |
$opt = $opt || {}; | |
if ($opt.enable) | |
this.enable($cssName, $opt.value); | |
$str = $str.replace(/((?:[^,{]+,?)*)\s*{([^}]+)}/g, condition+" $1 {$2}"); | |
$str = $str.replace(/,/g, ","+condition+" "); | |
this.set($str); | |
}, | |
pull : function ($cssName) { | |
return css_enabled[$cssName] || null; | |
}, | |
enable : function ($cssName, $value) { | |
$value = $value || 'yes'; | |
root.setAttribute('enable-css-'+ $cssName, $value); | |
css_enabled[$cssName] = $value; | |
}, | |
disable : function ($cssName) { | |
root.removeAttribute('enable-css-'+ $cssName); | |
delete css_enabled[$cssName]; | |
}, | |
// enable CSS when specific condition is meet | |
triger : function ($when, $str) { | |
}, | |
select : function ($str) { | |
return $$d.querySelector(this.get($str)); | |
}, | |
selectAll : function ($str) { | |
return Array.prototype.slice.apply($$d.querySelectorAll(this.get($str))); | |
}, | |
getMediaQueriesWidth : _getMediaQueriesWidth, | |
extendDictionary : function ($dic) { | |
this.dictionary = this.dictionary.concat($dic); | |
} | |
}; | |
return extend(instance, $c); | |
})(); | |
// manipulate cookies | |
var cookies = (function () { | |
return {}; | |
})(); | |
// the Class that process url change | |
var $$url = (function () { | |
var _url = formatUrl(), | |
urlChangeTasks = [], | |
hashChangeTasks = []; | |
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($isHashChange) { | |
console.debug('Kissogram Toolkit: URL Changed!'); | |
if ($isHashChange) | |
each(hashChangeTasks); | |
else | |
each(urlChangeTasks); | |
} | |
// mointor | |
var urlMonitor = setInterval(function () { | |
if (isUrlChanged()) | |
execTask(); | |
}, 500); | |
// bind onpopstate | |
window.addEventListener('popstate', function () { | |
if (isUrlChanged()) | |
execTask(); | |
}, false); | |
// hashchange | |
window.addEventListener('hashchange', function () { | |
execTask(true); | |
}, false); | |
var $c = { | |
onUrlChange : function ($func, $init) { | |
if ($init) | |
$func(); | |
urlChangeTasks.push($func); | |
}, | |
onHashChange : function ($func, $init) { | |
if ($init) | |
$func(); | |
hashChangeTasks.push($func); | |
}, | |
toString : function () { | |
return _url; | |
} | |
}; | |
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, Array.prototype.slice.apply(arguments), false); | |
$d.removeEventListener($evt, runOnceFunc); | |
}, { delay: $o.delay }), | |
newFunc = function () { | |
funcWithTiming.apply($d, Array.prototype.slice.apply(arguments)); | |
}; | |
return $o.runOnce ? runOnceFunc : newFunc ; | |
})() | |
); | |
}; | |
return function () { | |
// if $s is a element itself | |
var dom = (typeof $s == 'string') ? $$css.selectAll($s) : $s; | |
if (!utils.isArray(dom)) dom = [dom]; | |
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 ($e instanceof Array) | |
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)); | |
} | |
})(); | |
// 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); | |
} | |
// Main function begin ======================================== | |
// constructor | |
var $$c = function () { | |
}; | |
return extend($$c, { | |
"each" : each, | |
"extend" : extend, | |
"css" : $$css, | |
"listen" : listen, | |
"url" : $$url, | |
"click" : click, | |
"browser" : $$browser, | |
"window" : $$w, | |
"cookies" : cookies, | |
"select" : $$css.select, | |
"selectAll" : $$css.selectAll, | |
"tickTork" : setFunctionTiming, | |
"utils" : utils | |
}); | |
})(document); | |
var GooglePlusPlus = (function ($$d) { | |
console.debug('Google Plus Plus is now loaded!'); | |
var CSS_DICTIONARY = [ | |
[/<mainContent>/g, '.RazREb.ZI35oe'], | |
[/<streamContainer>/g, '.lzqA1d'], | |
[/<post>/g, '.lzqA1d .Wbhcze.Te'], | |
[/<selectedPost>/g, '.lzqA1d .Wbhcze.Te.bh'], | |
[/<leftSideNavigation>/g, '.ZXy9Tc.VR8ITe'], | |
[/<chatBox>/g, '.AOaixb.yRwDId.eub5Ne'], // maxmized chatbox | |
[/<pinColAttr>/g, 'pinter-col'], | |
[/<contentPane>/g, '#contentPane'], | |
[/<streamMoreButton>/g, '.E4V1D.fFgAkc'], | |
[/<streamMoreButtonContainer>/g,'.CfrSGe.WY2Dce.gH'], | |
[/<postInnerVideo>/g,'.Bz2yq .CEhkv.yPrMVe > iframe'], | |
[/<postInnerImageThumbnail>/g, '.yqrXXd.cPNrJ'], | |
[/<postHotonGooglePlusBanner>/g, '.SM0yVc.Ts'], | |
[/<postMainContent>/g, '.CPLjOe'], | |
[/<postInnerImageContainer>/g, '.jrj52d'], | |
[/<videoThumbnail>/g, '.CEhkv.yPrMVe > .t0Scwf.r4gluf'], | |
[/<singlePostProfileBlock>/g, '.kM5Oeb-OKUawf.hsHREd'], | |
[/<trendingBox>/g, '.L9ru2b.c-wa-Da'], | |
[/<shareBox>/g, '.v1WCLe'] | |
]; | |
var HIDE_CHATBOX = "hide-chatbox", HIDE_LEFT_SIDE_NAVIGATION = "hide-left-side-navigation", DISPLAY_CIRCLE_INFO = 'display-circle-info'; | |
var css = new $K.css(CSS_DICTIONARY); | |
// hide the left side menu | |
css.push(HIDE_LEFT_SIDE_NAVIGATION, | |
'<leftSideNavigation> { \ | |
left: -90px; \ | |
transition: left .8s ease; \ | |
-moz-transition: left .8s ease; \ | |
-webkit-transition: left .8s ease; \ | |
}', { enable : true } | |
); | |
css.push(HIDE_LEFT_SIDE_NAVIGATION, '<leftSideNavigation>:hover { left: 0px; }', { enable : true }); | |
css.push(HIDE_LEFT_SIDE_NAVIGATION, '.EziK2e, .atQfbd { margin-left:0px !important; }', { enable : true }); | |
// hide the chatbox | |
css.push(HIDE_CHATBOX, | |
'<chatBox> { \ | |
transition: right .8s ease; \ | |
-moz-transition: right .8s ease; \ | |
-webkit-transition: right .8s ease; \ | |
right : -160px; \ | |
z-index: 980; \ | |
}', | |
{ enable : true } | |
); | |
css.push(HIDE_CHATBOX, '<chatBox>:hover { right: 0px; }', { enable : true }); | |
css.push(HIDE_CHATBOX, '.EziK2e.ZeMNWc, .uWF1Ke.atQfbd.GjYYId { margin-right:65px; }', { enable : true }); | |
// new style that enable auto hiding the left side menu | |
css.set( | |
// place the navigation buttons on the black bar | |
'@media (min-width: 1450px) {'+ | |
'body[black-navigation-exist] .k-YB-nb.k-YB-nb-Zg ~ #content nav[role="navigation"] {'+ | |
'position:fixed;'+ | |
'top: -14px;'+ | |
'left: 750px;'+ | |
'z-index: 990;'+ | |
'background-color: transparent;'+ | |
'}'+ | |
'body[black-navigation-exist] .k-YB-nb.k-YB-nb-Zg ~ #content nav[role="navigation"] .fn.c-wa-Da.nhsZYd {'+ | |
'color: white;'+ // Profile name | |
'}'+ | |
'.NeLhq.fsfr2c {'+ | |
'background-color: transparent;'+ | |
'height: 0;'+ | |
'border-bottom : 0px;'+ | |
'}'+ | |
'.z-ea-n[role="listbox"], .urrzYd.c-o[role="menu"] { z-index: 995 }'+ // setting menu | |
'}' | |
); | |
// basic css for pinterest style | |
css.set( | |
// put the user photo into post | |
'<post> .Ns { margin-left:50px; margin-top:10px; }'+ | |
'<post> .Vl.EHlA9 { margin-top:20px; }'+ | |
'<post> .MrKoMd { background: transparent; }'+ | |
'<post> .Ye > a.k-Qf-C-RySO6d.oP{ display:inline; float:left; margin-left:80px; margin-top:10px; }'+ | |
// photo end | |
'<streamContainer> { padding:20px; }'+ | |
'<contentPane> { height:100% } '+ // main content | |
'<selectedPost> { z-index: 1 }'+ // top the locked post | |
'.vix0xf.VZAKvf { z-index:1; }'+ // make the comment panel cover the pictures | |
'.r4gluf, .VepHtd { background-color: white !important }' + // white backgroud for photos | |
'.SG.rnILlb[guidedhelpid="streamcontent"] { margin-top:75px }'+ // low the stream on profile page | |
'.SG[guidedhelpid="streamcontent"] { margin-left:-120px }'+ | |
'.HX2qEd { min-height: 95px }'+ // live where - profile page | |
'.vmfs3b { border-bottom: 1px solid #ccc; }'+ // the line under the buttons | |
'.x7iMqd.VG.xGUyuf { margin-left : 10% }'+ // "someone hasn't share anything with you" | |
'.kM5Oeb-RxwvHe.YleYsd.o8dQjb { margin-left:-250px; margin-top:-100px}'+ // 'block someone' button on profile page | |
'.kM5Oeb-OKUawf.hsHREd { margin-top: -40px }'+ // photo on profile page | |
'{mainContent} .vcard, .oJ8yxb.kSwsJc, .jbhX0d.YQqIy { margin-top: 90px }' // profile page | |
); | |
// deal with the trending box | |
css.set('<trendingBox> { display:none }'); | |
css.push(DISPLAY_CIRCLE_INFO, | |
'{mainContent} .vcard <trendingBox> {'+ // 'xxx' have you in circle | |
'display:block !important; margin-top:10px; z-index:10; background-color:white; border:1px solid #ccc;'+ | |
'}' | |
); | |
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; | |
}; | |
})(); | |
/* | |
the main function calculating all the elements' top value. | |
$selector : CSS selector of selecting all child elements | |
$contentWidth : current content width, | |
$options : { | |
initTop, initLeft : fisrt element's top / left | |
paddingH, paddingV : the horizontal / vertical padding between bricks, default is 10px | |
} | |
*/ | |
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 = 80, // 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 = 3, // at least how many columns should be put into the container | |
shortestPostHeight = 199, | |
deletedPostHeight = 38, | |
reCalculateLayoutEachXPixels = 5, | |
SELECTED_POST_TOP_ATTR = 'target-postition-top', | |
currentColumn = 0, | |
maxContainerWidth = getMaxContainerWidth($K.window.size().width), | |
brickParameters = [], posts = []; | |
css.set( | |
'<mainContent>:not(.Z77zM), .oJ8yxb.kSwsJc { margin-left : '+ mainContentLeftMargin +'px; }' // center the main stream, except on the page 'photo from your phone' | |
); | |
// hide the post not ready yet | |
css.set( | |
'<post>:not([<pinColAttr>]) { left: -999px; }' | |
); | |
// post animation | |
css.set( | |
'<post> { \ | |
position: absolute; \ | |
transition: all 1.2s ease; \ | |
-moz-transition: all 1.2s ease; \ | |
-webkit-transition: all 1.2s ease; \ | |
} \ | |
<post>:first-of-type { \ | |
transition: left 1.2s ease; \ | |
-moz-transition: left 1.2s ease; \ | |
-webkit-transition: left 1.2s ease; \ | |
}' | |
); | |
// animation for selected post | |
css.set( | |
'<selectedPost> { \ | |
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,140,0, 0.75); \ | |
box-shadow:0 0 30px rgba(0,140,0, 0.95); \ | |
-moz-box-shadow:0 0 30px rgba(0,140,0, 0.95); \ | |
-webkit-box-shadow:0 0 30px rgba(0,140,0,0.95); \ | |
}' | |
); | |
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 = 'google-plus-pinterest-brick-para-index'; | |
if (!mediaQueriesEnable) | |
css.enable(BRICK_PARA_ATTR, index); | |
if (brickParameters[index]) | |
return brickParameters[index]; | |
var para = brickParameters[index] = getBrickParameters(getMaxContainerWidth($windowWidth)), css_string =''; | |
// for Chrome | |
if (mediaQueriesEnable) { | |
css_string += '@media (min-width:'+ | |
(index * reCalculateLayoutEachXPixels) + | |
'px) and (max-width:'+ | |
((index+1) * reCalculateLayoutEachXPixels -1) +'px) {'; | |
console.debug('Current window width is '+ $windowWidth +'. Apply css Media Queries : '+ css_string); | |
} | |
// set brick width | |
css_string +='<post>,'+ | |
'<postInnerVideo>, <postMainContent>, <postInnerImageContainer> {' + | |
'width:' + para.brickWidth +'px' + | |
'}'+ | |
'<postHotonGooglePlusBanner> {'+ | |
'width:' + (para.brickWidth-2) +'px' + | |
'}' + | |
'<postInnerImageThumbnail> {'+ | |
'max-width:' + para.brickWidth +'px' + | |
'}'; | |
// offest | |
css_string +='<singlePostProfileBlock> {'+ | |
'margin-left:'+ (para.brickWidth - postBlockWidth)+'px'+ | |
'}'; | |
//if (para.brickWidth > postBlockWidth) { | |
var _offsetLeft = Math.floor((para.brickWidth - postBlockWidth)/2); | |
// for video thumbnails | |
css_string += '<post> <videoThumbnail> { margin-left: '+ _offsetLeft +'px; }'; | |
//} | |
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(BRICK_PARA_ATTR, css_string, { enable : true, value : index }); | |
return para; | |
} | |
// get current max container width | |
function getMaxContainerWidth($windowWidth) { | |
var _chatBoxWidth = css.pull(HIDE_CHATBOX) ? hiddenChatBoxWidth : chatBoxWidth; | |
if (!css.select('<chatBox>')) | |
_chatBoxWidth = 0; | |
return $windowWidth - hiddenNavigationWidth - _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 ======= | |
css.set('.Ns { cursor: url(//ssl.gstatic.com/s2/oz/images/sge/openhand_8_8.cur),move; }'); | |
function dragAndMoveHandler_over(e, $opt) { | |
var elem = e.target; | |
console.debug($opt); | |
$opt.post.style.top = $opt.top; | |
$opt.post.style.left = $opt.left; | |
if (e.preventDefault) | |
e.preventDefault(); // Necessary. Allows us to drop. | |
e.dataTransfer.dropEffect = 'move'; // See the section on the DataTransfer object. | |
return false; | |
} | |
function dragAndMoveHandler_listener(e) { | |
var elem = e.target, post; | |
if (elem.tagName == "HEADER") | |
elem = elem.parentElement; | |
if (elem.className == "Ns") | |
post = elem.parentElement.parentElement.parentElement; | |
if (!(post && /Wbhcze Te/.test(post.className))) | |
return; | |
if (!elem.hasAttribute('draggable')) { | |
elem.setAttribute('draggable', 'true'); | |
elem.addEventListener('dragover', (function () { | |
//var x | |
var leftOffset = getComputedStyle(post).getPropertyValue("left"); | |
var topOffset = getComputedStyle(post).getPropertyValue("top"); | |
return function (e) { | |
dragAndMoveHandler_over(e, { "post" : post, "left": leftOffset, "top" : topOffset }); | |
}; | |
})(), false); | |
} | |
} | |
// drag and drop =========== | |
// re-layout the bricks | |
function replaceBricks($brickPara) { | |
var bricks = css.selectAll('<post>'), | |
brickAmount = bricks.length, | |
ignoreColumn = ($brickPara.brickColumn != currentColumn), | |
tops = [], needUpdate = []; | |
currentColumn = $brickPara.brickColumn; | |
for (var i=0; i<currentColumn; i++) { | |
tops.push(0); // default top | |
needUpdate.push(ignoreColumn); // if current column need to be updated | |
} | |
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 (!posts[postId] || bricks[i].style.top=="") { // init | |
needUpdate[curCol] = true; | |
posts[postId] = { | |
id : postId, | |
column : curCol, | |
height : postHeight, | |
toString : function () { | |
return this.id; | |
}, | |
locked : false, | |
selected : false | |
}; | |
} | |
else if (!ignoreColumn && !needReloadPost) | |
curCol = posts[postId].column; | |
else | |
posts[postId].column = curCol; | |
if (posts[postId].height != postHeight) { | |
needUpdate[curCol] = true; | |
posts[postId].height = postHeight; | |
} | |
// do not move the selected / locked post | |
if (/ bh/.test(bricks[i].className)) { | |
posts[postId].selected = true; | |
if ((bricks[i].style.top.replace('px','') || 0) != tops[curCol]) { | |
if (!bricks[i].hasAttribute(SELECTED_POST_TOP_ATTR)) | |
bricks[i].addEventListener('dblclick', function () { | |
if (this.hasAttribute(SELECTED_POST_TOP_ATTR)) { | |
this.style.top = this.getAttribute(SELECTED_POST_TOP_ATTR) +'px'; | |
this.removeAttribute(SELECTED_POST_TOP_ATTR); | |
this.className = this.className.replace(/ bh/,''); | |
} | |
}, false); | |
bricks[i].setAttribute(SELECTED_POST_TOP_ATTR, tops[curCol]); | |
} | |
} | |
else if (!posts[postId].locked) { | |
if (ignoreColumn || needReloadPost) | |
bricks[i].setAttribute(COLUMN_ATTR, curCol); // update column information | |
if (needUpdate[curCol] || posts[postId].selected) | |
bricks[i].style.top = tops[curCol]+'px'; | |
posts[postId].selected = false; | |
} | |
if (!posts[postId].locked) | |
tops[curCol] += postHeight + paddingV; | |
} | |
return Math.max.apply(Math, tops); // return total height | |
} | |
function appendBricks($brickPara) { | |
} | |
var layout = (function () { | |
var maxHeight = 0; | |
return function () { | |
//console.debug('G++ Pinterest: Re-layout at '+ new Date().getTime()); | |
var brickPara = loadBrickCSS(window.innerWidth); | |
var height = replaceBricks(brickPara); | |
// 'more' button | |
if (maxHeight != height) { | |
maxHeight = height; | |
css.select('<streamMoreButtonContainer>').style.marginTop = height+'px'; | |
} | |
}; | |
})(); | |
var timingLayout = $K.tickTork(layout, { interval : 2000, delay: 800 }); | |
function _init() { | |
$K.url.onUrlChange( | |
function () { | |
$K.listen(css.get('<streamContainer>'), 'DOMSubtreeModified', timingLayout, { init: true }); | |
}, true | |
); | |
$K.listen(css.get('<streamContainer>'), 'mousedown', dragAndMoveHandler_listener); | |
$K.window.onResize(timingLayout); | |
} | |
return { | |
"layout" : timingLayout, | |
"init" : _init | |
} | |
})(); | |
// cancel a bunch of action | |
$K.url.onUrlChange(function () { | |
if (css.select('#gbx3')) { | |
$K.listen('#gbx3', '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 shareBox = css.select('<shareBox>'); | |
//if (shareBox) | |
// shareBox.style.marginLeft = (window.innerWidth - 500) / 2 +'px'; // the width of share box is 500px | |
var Theme = GPinterest; | |
//$K.listen(css.get('<streamContainer>'),'click',function (){ | |
// $K.click(css.select('<streamMoreButton>')); | |
//}); | |
// a-sync | |
setTimeout(Theme.init, 10); | |
})(document); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment