Greasy Fork is available in English.

SFW

an anti-procrastination user script;拖延症矫正脚本

질문, 리뷰하거나, 이 스크립트를 신고하세요.
  1. // Copyright (c) 2013, heyeshuang. (MIT Licensed)
  2. // ==UserScript==
  3. // @id userscripts.org-a861f8c9-2981-4747-a244-a40ace20e46f@scriptish
  4. // @name SFW
  5. // @namespace https://github.com/heyeshuang/SFW
  6. // @version 0.2.0
  7. // @description an anti-procrastination user script;拖延症矫正脚本
  8. // @include http*://*
  9. // @run-at document-end
  10. // ==/UserScript==
  11. if (window.top != window.self) return; //don't run on frames or iframes
  12. (function( win, undefined ){
  13.  
  14. var doc = win.document,
  15. docElem = doc.documentElement;
  16.  
  17. var easyDialog = function(){
  18.  
  19. var body = doc.body,
  20. isIE = !-[1,], // 判断IE6/7/8 不能判断IE9
  21. isIE6 = isIE && /msie 6/.test( navigator.userAgent.toLowerCase() ), // 判断IE6
  22. uuid = 1,
  23. expando = 'cache' + ( +new Date() + "" ).slice( -8 ), // 生成随机数
  24. cacheData = {
  25. /**
  26. * 1 : {
  27. * eclick : [ handler1, handler2, handler3 ];
  28. * clickHandler : function(){ //... };
  29. * }
  30. */
  31. };
  32.  
  33. var Dialog = function(){};
  34.  
  35. Dialog.prototype = {
  36. // 参数设置
  37. getOptions : function( arg ){
  38. var i,
  39. options = {},
  40. // 默认参数
  41. defaults = {
  42. container: null, // string / object 弹处层内容的id或内容模板
  43. overlay: true, // boolean 是否添加遮罩层
  44. drag: true, // boolean 是否绑定拖拽事件
  45. fixed: true, // boolean 是否静止定位
  46. follow: null, // string / object 是否跟随自定义元素来定位
  47. followX: 0, // number 相对于自定义元素的X坐标的偏移
  48. followY: 0, // number 相对于自定义元素的Y坐标的偏移
  49. autoClose: 0, // number 自动关闭弹出层的时间
  50. lock: false, // boolean 是否允许ESC键来关闭弹出层
  51. callback: null // function 关闭弹出层后执行的回调函数
  52. /**
  53. * container为object时的参数格式
  54. * container : {
  55. * header : '弹出层标题',
  56. * content : '弹出层内容',
  57. * yesFn : function(){}, // 确定按钮的回调函数
  58. * noFn : function(){} / true, // 取消按钮的回调函数
  59. * yesText : '确定', // 确定按钮的文本,默认为‘确定’
  60. * noText : '取消' // 取消按钮的文本,默认为‘取消’
  61. * }
  62. */
  63. };
  64. for( i in defaults ){
  65. options[i] = arg[i] !== undefined ? arg[i] : defaults[i];
  66. }
  67. Dialog.data( 'options', options );
  68. return options;
  69. },
  70. // 防止IE6模拟fixed时出现抖动
  71. setBodyBg : function(){
  72. if( body.currentStyle.backgroundAttachment !== 'fixed' ){
  73. body.style.backgroundImage = 'url(about:blank)';
  74. body.style.backgroundAttachment = 'fixed';
  75. }
  76. },
  77. // 防止IE6的select穿透
  78. appendIframe : function(elem){
  79. elem.innerHTML = '<iframe style="position:absolute;left:0;top:0;width:100%;height:100%;z-index:-1;border:0 none;filter:alpha(opacity=0)"></iframe>';
  80. },
  81. /**
  82. * 设置元素跟随定位
  83. * @param { Object } 跟随的DOM元素
  84. * @param { String / Object } 被跟随的DOM元素
  85. * @param { Number } 相对于被跟随元素的X轴的偏移
  86. * @param { Number } 相对于被跟随元素的Y轴的偏移
  87. */
  88. setFollow : function( elem, follow, x, y ){
  89. follow = typeof follow === 'string' ? doc.getElementById( follow ) : follow;
  90. var style = elem.style;
  91. style.position = 'absolute';
  92. style.left = Dialog.getOffset( follow, 'left') + x + 'px';
  93. style.top = Dialog.getOffset( follow, 'top' ) + y + 'px';
  94. },
  95. /**
  96. * 设置元素固定(fixed) / 绝对(absolute)定位
  97. * @param { Object } DOM元素
  98. * @param { Boolean } true : fixed, fasle : absolute
  99. */
  100. setPosition : function( elem, fixed ){
  101. var style = elem.style;
  102. style.position = isIE6 ? 'absolute' : fixed ? 'fixed' : 'absolute';
  103. if( fixed ){
  104. if( isIE6 ){
  105. style.setExpression( 'top','fuckIE6=document.documentElement.scrollTop+document.documentElement.clientHeight/2+"px"' );
  106. }
  107. else{
  108. style.top = '50%';
  109. }
  110. style.left = '50%';
  111. }
  112. else{
  113. if( isIE6 ){
  114. style.removeExpression( 'top' );
  115. }
  116. style.top = docElem.clientHeight/2 + Dialog.getScroll( 'top' ) + 'px';
  117. style.left = docElem.clientWidth/2 + Dialog.getScroll( 'left' ) + 'px';
  118. }
  119. },
  120. /**
  121. * 创建遮罩层
  122. * @return { Object } 遮罩层
  123. */
  124. createOverlay : function(){
  125. var overlay = doc.createElement('div'),
  126. style = overlay.style;
  127. style.cssText = 'margin:0;padding:0;border:none;width:100%;height:100%;background:#333;opacity:0.6;filter:alpha(opacity=60);z-index:9999;position:fixed;top:0;left:0;';
  128. // IE6模拟fixed
  129. if(isIE6){
  130. body.style.height = '100%';
  131. style.position = 'absolute';
  132. style.setExpression('top','fuckIE6=document.documentElement.scrollTop+"px"');
  133. }
  134. overlay.id = 'overlay';
  135. return overlay;
  136. },
  137. /**
  138. * 创建弹出层
  139. * @return { Object } 弹出层
  140. */
  141. createDialogBox : function(){
  142. var dialogBox = doc.createElement('div');
  143. dialogBox.style.cssText = 'margin:0;padding:0;border:none;z-index:10000;';
  144. dialogBox.id = 'easyDialogBox';
  145. return dialogBox;
  146. },
  147.  
  148. /**
  149. * 创建默认的弹出层内容模板
  150. * @param { Object } 模板参数
  151. * @return { Object } 弹出层内容模板
  152. */
  153. createDialogWrap : function( tmpl ){
  154. // 弹出层标题
  155. var header = tmpl.header ?
  156. '<h4 class="easyDialog_title" id="easyDialogTitle"><a href="javascript:void(0)" title="关闭窗口" class="close_btn" id="closeBtn">&times;</a>' + tmpl.header + '</h4>' :
  157. '',
  158. // 确定按钮
  159. yesBtn = typeof tmpl.yesFn === 'function' ?
  160. '<button class="btn_highlight" id="easyDialogYesBtn">' + ( typeof tmpl.yesText === 'string' ? tmpl.yesText : '确定' ) + '</button>' :
  161. '',
  162. // 取消按钮
  163. noBtn = typeof tmpl.noFn === 'function' || tmpl.noFn === true ?
  164. '<button class="btn_normal" id="easyDialogNoBtn">' + ( typeof tmpl.noText === 'string' ? tmpl.noText : '取消' ) + '</button>' :
  165. '',
  166. // footer
  167. footer = yesBtn === '' && noBtn === '' ? '' :
  168. '<div class="easyDialog_footer">' + noBtn + yesBtn + '</div>',
  169. dialogTmpl = [
  170. '<div class="easyDialog_content">',
  171. header,
  172. '<div class="easyDialog_text">' + tmpl.content + '</div>',
  173. footer,
  174. '</div>'
  175. ].join(''),
  176.  
  177. dialogWrap = doc.getElementById( 'easyDialogWrapper' ),
  178. rScript = /<[\/]*script[\s\S]*?>/ig;
  179. if( !dialogWrap ){
  180. dialogWrap = doc.createElement( 'div' );
  181. dialogWrap.id = 'easyDialogWrapper';
  182. dialogWrap.className = 'easyDialog_wrapper';
  183. }
  184. dialogWrap.innerHTML = dialogTmpl.replace( rScript, '' );
  185. return dialogWrap;
  186. }
  187. };
  188. /**
  189. * 设置并返回缓存的数据 关于缓存系统详见:http://stylechen.com/cachedata.html
  190. * @param { String / Object } 任意字符串或DOM元素
  191. * @param { String } 缓存属性名
  192. * @param { Anything } 缓存属性值
  193. * @return { Object }
  194. */
  195. Dialog.data = function( elem, val, data ){
  196. if( typeof elem === 'string' ){
  197. if( val !== undefined ){
  198. cacheData[elem] = val;
  199. }
  200. return cacheData[elem];
  201. }
  202. else if( typeof elem === 'object' ){
  203. // 如果是window、document将不添加自定义属性
  204. // window的索引是0 document索引为1
  205. var index = elem === win ? 0 :
  206. elem.nodeType === 9 ? 1 :
  207. elem[expando] ? elem[expando] :
  208. (elem[expando] = ++uuid),
  209. thisCache = cacheData[index] ? cacheData[index] : ( cacheData[index] = {} );
  210. if( data !== undefined ){
  211. // 将数据存入缓存中
  212. thisCache[val] = data;
  213. }
  214. // 返回DOM元素存储的数据
  215. return thisCache[val];
  216. }
  217. };
  218.  
  219. /**
  220. * 删除缓存
  221. * @param { String / Object } 任意字符串或DOM元素
  222. * @param { String } 要删除的缓存属性名
  223. */
  224. Dialog.removeData = function( elem, val ){
  225. if( typeof elem === 'string' ){
  226. delete cacheData[elem];
  227. }
  228. else if( typeof elem === 'object' ){
  229. var index = elem === win ? 0 :
  230. elem.nodeType === 9 ? 1 :
  231. elem[expando];
  232. if( index === undefined ) return;
  233. // 检测对象是否为空
  234. var isEmptyObject = function( obj ) {
  235. var name;
  236. for ( name in obj ) {
  237. return false;
  238. }
  239. return true;
  240. },
  241. // 删除DOM元素所有的缓存数据
  242. delteProp = function(){
  243. delete cacheData[index];
  244. if( index <= 1 ) return;
  245. try{
  246. // IE8及标准浏览器可以直接使用delete来删除属性
  247. delete elem[expando];
  248. }
  249. catch ( e ) {
  250. // IE6/IE7使用removeAttribute方法来删除属性(document会报错)
  251. elem.removeAttribute( expando );
  252. }
  253. };
  254.  
  255. if( val ){
  256. // 只删除指定的数据
  257. delete cacheData[index][val];
  258. if( isEmptyObject( cacheData[index] ) ){
  259. delteProp();
  260. }
  261. }
  262. else{
  263. delteProp();
  264. }
  265. }
  266. };
  267.  
  268. // 事件处理系统
  269. Dialog.event = {
  270. bind : function( elem, type, handler ){
  271. var events = Dialog.data( elem, 'e' + type ) || Dialog.data( elem, 'e' + type, [] );
  272. // 将事件函数添加到缓存中
  273. events.push( handler );
  274. // 同一事件类型只注册一次事件,防止重复注册
  275. if( events.length === 1 ){
  276. var eventHandler = this.eventHandler( elem );
  277. Dialog.data( elem, type + 'Handler', eventHandler );
  278. if( elem.addEventListener ){
  279. elem.addEventListener( type, eventHandler, false );
  280. }
  281. else if( elem.attachEvent ){
  282. elem.attachEvent( 'on' + type, eventHandler );
  283. }
  284. }
  285. },
  286. unbind : function( elem, type, handler ){
  287. var events = Dialog.data( elem, 'e' + type );
  288. if( !events ) return;
  289. // 如果没有传入要删除的事件处理函数则删除该事件类型的缓存
  290. if( !handler ){
  291. events = undefined;
  292. }
  293. // 如果有具体的事件处理函数则只删除一个
  294. else{
  295. for( var i = events.length - 1, fn = events[i]; i >= 0; i-- ){
  296. if( fn === handler ){
  297. events.splice( i, 1 );
  298. }
  299. }
  300. }
  301. // 删除事件和缓存
  302. if( !events || !events.length ){
  303. var eventHandler = Dialog.data( elem, type + 'Handler' );
  304. if( elem.addEventListener ){
  305. elem.removeEventListener( type, eventHandler, false );
  306. }
  307. else if( elem.attachEvent ){
  308. elem.detachEvent( 'on' + type, eventHandler );
  309. }
  310. Dialog.removeData( elem, type + 'Handler' );
  311. Dialog.removeData( elem, 'e' + type );
  312. }
  313. },
  314. // 依次执行事件绑定的函数
  315. eventHandler : function( elem ){
  316. return function( event ){
  317. event = Dialog.event.fixEvent( event || win.event );
  318. var type = event.type,
  319. events = Dialog.data( elem, 'e' + type );
  320. for( var i = 0, handler; handler = events[i++]; ){
  321. if( handler.call(elem, event) === false ){
  322. event.preventDefault();
  323. event.stopPropagation();
  324. }
  325. }
  326. }
  327. },
  328. // 修复IE浏览器支持常见的标准事件的API
  329. fixEvent : function( e ){
  330. // 支持DOM 2级标准事件的浏览器无需做修复
  331. if ( e.target ) return e;
  332. var event = {}, name;
  333. event.target = e.srcElement || document;
  334. event.preventDefault = function(){
  335. e.returnValue = false;
  336. };
  337. event.stopPropagation = function(){
  338. e.cancelBubble = true;
  339. };
  340. // IE6/7/8在原生的window.event中直接写入自定义属性
  341. // 会导致内存泄漏,所以采用复制的方式
  342. for( name in e ){
  343. event[name] = e[name];
  344. }
  345. return event;
  346. }
  347. };
  348.  
  349. /**
  350. * 首字母大写转换
  351. * @param { String } 要转换的字符串
  352. * @return { String } 转换后的字符串 top => Top
  353. */
  354. Dialog.capitalize = function( str ){
  355. var firstStr = str.charAt(0);
  356. return firstStr.toUpperCase() + str.replace( firstStr, '' );
  357. };
  358.  
  359. /**
  360. * 获取滚动条的位置
  361. * @param { String } 'top' & 'left'
  362. * @return { Number }
  363. */
  364. Dialog.getScroll = function( type ){
  365. var upType = this.capitalize( type );
  366. return docElem['scroll' + upType] || body['scroll' + upType];
  367. };
  368.  
  369. /**
  370. * 获取元素在页面中的位置
  371. * @param { Object } DOM元素
  372. * @param { String } 'top' & 'left'
  373. * @return { Number }
  374. */
  375. Dialog.getOffset = function( elem, type ){
  376. var upType = this.capitalize( type ),
  377. client = docElem['client' + upType] || body['client' + upType] || 0,
  378. scroll = this.getScroll( type ),
  379. box = elem.getBoundingClientRect();
  380. return Math.round( box[type] ) + scroll - client;
  381. };
  382.  
  383. /**
  384. * 拖拽效果
  385. * @param { Object } 触发拖拽的DOM元素
  386. * @param { Object } 要进行拖拽的DOM元素
  387. */
  388. Dialog.drag = function( target, moveElem ){
  389. // 清除文本选择
  390. var clearSelect = 'getSelection' in win ? function(){
  391. win.getSelection().removeAllRanges();
  392. } : function(){
  393. try{
  394. doc.selection.empty();
  395. }
  396. catch( e ){};
  397. },
  398. self = this,
  399. event = self.event,
  400. isDown = false,
  401. newElem = isIE ? target : doc,
  402. fixed = moveElem.style.position === 'fixed',
  403. _fixed = Dialog.data( 'options' ).fixed;
  404. // mousedown
  405. var down = function( e ){
  406. isDown = true;
  407. var scrollTop = self.getScroll( 'top' ),
  408. scrollLeft = self.getScroll( 'left' ),
  409. edgeLeft = fixed ? 0 : scrollLeft,
  410. edgeTop = fixed ? 0 : scrollTop;
  411. Dialog.data( 'dragData', {
  412. x : e.clientX - self.getOffset( moveElem, 'left' ) + ( fixed ? scrollLeft : 0 ),
  413. y : e.clientY - self.getOffset( moveElem, 'top' ) + ( fixed ? scrollTop : 0 ),
  414. // 设置上下左右4个临界点的位置
  415. // 固定定位的临界点 = 当前屏的宽、高(下、右要减去元素本身的宽度或高度)
  416. // 绝对定位的临界点 = 当前屏的宽、高 + 滚动条卷起部分(下、右要减去元素本身的宽度或高度)
  417. el : edgeLeft, // 左临界点
  418. et : edgeTop, // 上临界点
  419. er : edgeLeft + docElem.clientWidth - moveElem.offsetWidth, // 右临界点
  420. eb : edgeTop + docElem.clientHeight - moveElem.offsetHeight // 下临界点
  421. });
  422. if( isIE ){
  423. // IE6如果是模拟fixed在mousedown的时候先删除模拟,节省性能
  424. if( isIE6 && _fixed ){
  425. moveElem.style.removeExpression( 'top' );
  426. }
  427. target.setCapture();
  428. }
  429. event.bind( newElem, 'mousemove', move );
  430. event.bind( newElem, 'mouseup', up );
  431. if( isIE ){
  432. event.bind( target, 'losecapture', up );
  433. }
  434. e.stopPropagation();
  435. e.preventDefault();
  436. };
  437. event.bind( target, 'mousedown', down );
  438. // mousemove
  439. var move = function( e ){
  440. if( !isDown ) return;
  441. clearSelect();
  442. var dragData = Dialog.data( 'dragData' ),
  443. left = e.clientX - dragData.x,
  444. top = e.clientY - dragData.y,
  445. et = dragData.et,
  446. er = dragData.er,
  447. eb = dragData.eb,
  448. el = dragData.el,
  449. style = moveElem.style;
  450. // 设置上下左右的临界点以防止元素溢出当前屏
  451. style.marginLeft = style.marginTop = '0px';
  452. style.left = ( left <= el ? el : (left >= er ? er : left) ) + 'px';
  453. style.top = ( top <= et ? et : (top >= eb ? eb : top) ) + 'px';
  454. e.stopPropagation();
  455. };
  456. // mouseup
  457. var up = function( e ){
  458. isDown = false;
  459. if( isIE ){
  460. event.unbind( target, 'losecapture', arguments.callee );
  461. }
  462. event.unbind( newElem, 'mousemove', move );
  463. event.unbind( newElem, 'mouseup', arguments.callee );
  464. if( isIE ){
  465. target.releaseCapture();
  466. // IE6如果是模拟fixed在mouseup的时候要重新设置模拟
  467. if( isIE6 && _fixed ){
  468. var top = parseInt( moveElem.style.top ) - self.getScroll( 'top' );
  469. moveElem.style.setExpression('top',"fuckIE6=document.documentElement.scrollTop+" + top + '+"px"');
  470. }
  471. }
  472. e.stopPropagation();
  473. };
  474. };
  475.  
  476. var timer, // 定时器
  477. // ESC键关闭弹出层
  478. escClose = function( e ){
  479. if( e.keyCode === 27 ){
  480. extend.close();
  481. }
  482. },
  483. // 清除定时器
  484. clearTimer = function(){
  485. if( timer ){
  486. clearTimeout( timer );
  487. timer = undefined;
  488. }
  489. };
  490. var extend = {
  491. open : function(){
  492. var $ = new Dialog(),
  493. options = $.getOptions( arguments[0] || {} ), // 获取参数
  494. event = Dialog.event,
  495. docWidth = docElem.clientWidth,
  496. docHeight = docElem.clientHeight,
  497. self = this,
  498. overlay,
  499. dialogBox,
  500. dialogWrap,
  501. boxChild;
  502. clearTimer();
  503. // ------------------------------------------------------
  504. // ---------------------插入遮罩层-----------------------
  505. // ------------------------------------------------------
  506. // 如果页面中已经缓存遮罩层,直接显示
  507. if( options.overlay ){
  508. overlay = doc.getElementById( 'overlay' );
  509. if( !overlay ){
  510. overlay = $.createOverlay();
  511. body.appendChild( overlay );
  512. if( isIE6 ){
  513. $.appendIframe( overlay );
  514. }
  515. }
  516. overlay.style.display = 'block';
  517. }
  518. if(isIE6){
  519. $.setBodyBg();
  520. }
  521. // ------------------------------------------------------
  522. // ---------------------插入弹出层-----------------------
  523. // ------------------------------------------------------
  524. // 如果页面中已经缓存弹出层,直接显示
  525. dialogBox = doc.getElementById( 'easyDialogBox' );
  526. if( !dialogBox ){
  527. dialogBox = $.createDialogBox();
  528. body.appendChild( dialogBox );
  529. }
  530. if( options.follow ){
  531. var follow = function(){
  532. $.setFollow( dialogBox, options.follow, options.followX, options.followY );
  533. };
  534. follow();
  535. event.bind( win, 'resize', follow );
  536. Dialog.data( 'follow', follow );
  537. if( overlay ){
  538. overlay.style.display = 'none';
  539. }
  540. options.fixed = false;
  541. }
  542. else{
  543. $.setPosition( dialogBox, options.fixed );
  544. }
  545. dialogBox.style.display = 'block';
  546. // ------------------------------------------------------
  547. // -------------------插入弹出层内容---------------------
  548. // ------------------------------------------------------
  549. // 判断弹出层内容是否已经缓存过
  550. dialogWrap = typeof options.container === 'string' ?
  551. doc.getElementById( options.container ) :
  552. $.createDialogWrap( options.container );
  553. boxChild = dialogBox.getElementsByTagName('*')[0];
  554. if( !boxChild ){
  555. dialogBox.appendChild( dialogWrap );
  556. }
  557. else if( boxChild && dialogWrap !== boxChild ){
  558. boxChild.style.display = 'none';
  559. body.appendChild( boxChild );
  560. dialogBox.appendChild( dialogWrap );
  561. }
  562. dialogWrap.style.display = 'block';
  563. var eWidth = dialogWrap.offsetWidth,
  564. eHeight = dialogWrap.offsetHeight,
  565. widthOverflow = eWidth > docWidth,
  566. heigthOverflow = eHeight > docHeight;
  567. // 强制去掉自定义弹出层内容的margin
  568. dialogWrap.style.marginTop = dialogWrap.style.marginRight = dialogWrap.style.marginBottom = dialogWrap.style.marginLeft = '0px';
  569. // 居中定位
  570. if( !options.follow ){
  571. dialogBox.style.marginLeft = '-' + (widthOverflow ? docWidth/2 : eWidth/2) + 'px';
  572. dialogBox.style.marginTop = '-' + (heigthOverflow ? docHeight/2 : eHeight/2) + 'px';
  573. }
  574. else{
  575. dialogBox.style.marginLeft = dialogBox.style.marginTop = '0px';
  576. }
  577. // 防止select穿透固定宽度和高度
  578. if( isIE6 && !options.overlay ){
  579. dialogBox.style.width = eWidth + 'px';
  580. dialogBox.style.height = eHeight + 'px';
  581. }
  582. // ------------------------------------------------------
  583. // --------------------绑定相关事件----------------------
  584. // ------------------------------------------------------
  585. var closeBtn = doc.getElementById( 'closeBtn' ),
  586. dialogTitle = doc.getElementById( 'easyDialogTitle' ),
  587. dialogYesBtn = doc.getElementById('easyDialogYesBtn'),
  588. dialogNoBtn = doc.getElementById('easyDialogNoBtn');
  589.  
  590. // 绑定确定按钮的回调函数
  591. if( dialogYesBtn ){
  592. event.bind( dialogYesBtn, 'click', function( event ){
  593. if( options.container.yesFn.call(self, event) !== false ){
  594. self.close();
  595. }
  596. });
  597. }
  598. // 绑定取消按钮的回调函数
  599. if( dialogNoBtn ){
  600. var noCallback = function( event ){
  601. if( options.container.noFn === true || options.container.noFn.call(self, event) !== false ){
  602. self.close();
  603. }
  604. };
  605. event.bind( dialogNoBtn, 'click', noCallback );
  606. // 如果取消按钮有回调函数 关闭按钮也绑定同样的回调函数
  607. if( closeBtn ){
  608. event.bind( closeBtn, 'click', noCallback );
  609. }
  610. }
  611. // 关闭按钮绑定事件
  612. else if( closeBtn ){
  613. event.bind( closeBtn, 'click', self.close );
  614. }
  615. // ESC键关闭弹出层
  616. if( !options.lock ){
  617. event.bind( doc, 'keyup', escClose );
  618. }
  619. // 自动关闭弹出层
  620. if( options.autoClose && typeof options.autoClose === 'number' ){
  621. timer = setTimeout( self.close, options.autoClose );
  622. }
  623. // 绑定拖拽(如果弹出层内容的宽度或高度溢出将不绑定拖拽)
  624. if( options.drag && dialogTitle && !widthOverflow && !heigthOverflow ){
  625. dialogTitle.style.cursor = 'move';
  626. Dialog.drag( dialogTitle, dialogBox );
  627. }
  628. // 确保弹出层绝对定位时放大缩小窗口也可以垂直居中显示
  629. if( !options.follow && !options.fixed ){
  630. var resize = function(){
  631. $.setPosition( dialogBox, false );
  632. };
  633. // 如果弹出层内容的宽度或高度溢出将不绑定resize事件
  634. if( !widthOverflow && !heigthOverflow ){
  635. event.bind( win, 'resize', resize );
  636. }
  637. Dialog.data( 'resize', resize );
  638. }
  639. // 缓存相关元素以便关闭弹出层的时候进行操作
  640. Dialog.data( 'dialogElements', {
  641. overlay : overlay,
  642. dialogBox : dialogBox,
  643. closeBtn : closeBtn,
  644. dialogTitle : dialogTitle,
  645. dialogYesBtn : dialogYesBtn,
  646. dialogNoBtn : dialogNoBtn
  647. });
  648. },
  649. close : function(){
  650. var options = Dialog.data( 'options' ),
  651. elements = Dialog.data( 'dialogElements' ),
  652. event = Dialog.event;
  653. clearTimer();
  654. // 隐藏遮罩层
  655. if( options.overlay && elements.overlay ){
  656. elements.overlay.style.display = 'none';
  657. }
  658. // 隐藏弹出层
  659. elements.dialogBox.style.display = 'none';
  660. // IE6清除CSS表达式
  661. if( isIE6 ){
  662. elements.dialogBox.style.removeExpression( 'top' );
  663. }
  664. // ------------------------------------------------------
  665. // --------------------删除相关事件----------------------
  666. // ------------------------------------------------------
  667. if( elements.closeBtn ){
  668. event.unbind( elements.closeBtn, 'click' );
  669. }
  670.  
  671. if( elements.dialogTitle ){
  672. event.unbind( elements.dialogTitle, 'mousedown' );
  673. }
  674. if( elements.dialogYesBtn ){
  675. event.unbind( elements.dialogYesBtn, 'click' );
  676. }
  677. if( elements.dialogNoBtn ){
  678. event.unbind( elements.dialogNoBtn, 'click' );
  679. }
  680. if( !options.follow && !options.fixed ){
  681. event.unbind( win, 'resize', Dialog.data('resize') );
  682. Dialog.removeData( 'resize' );
  683. }
  684. if( options.follow ){
  685. event.unbind( win, 'resize', Dialog.data('follow') );
  686. Dialog.removeData( 'follow' );
  687. }
  688. if( !options.lock ){
  689. event.unbind( doc, 'keyup', escClose );
  690. }
  691. // 执行callback
  692. if(typeof options.callback === 'function'){
  693. options.callback.call( extend );
  694. }
  695. // 清除缓存
  696. Dialog.removeData( 'options' );
  697. Dialog.removeData( 'dialogElements' );
  698. }
  699. };
  700.  
  701. return extend;
  702.  
  703. };
  704.  
  705. // ------------------------------------------------------
  706. // ---------------------DOM加载模块----------------------
  707. // ------------------------------------------------------
  708. var loaded = function(){
  709. win.easyDialog = easyDialog();
  710. },
  711. doScrollCheck = function(){
  712. if ( doc.body ) return;
  713.  
  714. try {
  715. docElem.doScroll("left");
  716. } catch(e) {
  717. setTimeout( doScrollCheck, 1 );
  718. return;
  719. }
  720. loaded();
  721. };
  722.  
  723. (function(){
  724. if( doc.body ){
  725. loaded();
  726. }
  727. else{
  728. if( doc.addEventListener ){
  729. doc.addEventListener( 'DOMContentLoaded', function(){
  730. doc.removeEventListener( 'DOMContentLoaded', arguments.callee, false );
  731. loaded();
  732. }, false );
  733. win.addEventListener( 'load', loaded, false );
  734. }
  735. else if( doc.attachEvent ){
  736. doc.attachEvent( 'onreadystatechange', function(){
  737. if( doc.readyState === 'complete' ){
  738. doc.detachEvent( 'onreadystatechange', arguments.callee );
  739. loaded();
  740. }
  741. });
  742. win.attachEvent( 'onload', loaded );
  743. var toplevel = false;
  744. try {
  745. toplevel = win.frameElement == null;
  746. } catch(e) {}
  747.  
  748. if ( docElem.doScroll && toplevel ) {
  749. doScrollCheck();
  750. }
  751. }
  752. }
  753. })();
  754.  
  755. })( window, undefined );
  756.  
  757. // 2012-04-12 修复跟随定位缩放浏览器时无法继续跟随的BUG
  758. // 2012-04-22 修复弹出层内容的尺寸大于浏览器当前屏尺寸的BUG
  759. (function() {
  760. var cssContainer, cssContent, error;
  761.  
  762. cssContent = ' .pinned{\n padding:3px;\n line-height:0;\n opacity:0.7;\n background: #afb4db;\n border-radius: 20px;\n border:0px solid red;\n position:fixed;\n left:10px;\n bottom:10px;\n }\n\n .invisible{\n display:none;\n cursor: default;\n }\n #configBox{\n width:320px;\n }\n\nbutton::-moz-focus-inner{\nborder:0;\npadding:0;\nmargin:0;\n}\n\n.easyDialog_wrapper{\ncolor:#444;\nborder:3px solid rgba(0,0,0,0);\n-webkit-border-radius:5px;\n-moz-border-radius:5px;\nborder-radius:5px;\n-webkit-box-shadow:0 0 10px rgba(0,0,0,0.4);\n-moz-box-shadow:0 0 10px rgba(0,0,0,0.4);\nbox-shadow:0 0 10px rgba(0,0,0,0.4);\ndisplay:none;\nfont-family:"Microsoft yahei", Arial;\n}\n\n.easyDialog_wrapper .easyDialog_content{\n-webkit-border-radius:4px;\n-moz-border-radius:4px;\nborder-radius:4px;\nbackground:#fff;\nborder:1px solid #e5e5e5;\n}\n\n.easyDialog_wrapper .easyDialog_title{\nheight:30px;\nline-height:30px;\noverflow:hidden;\ncolor:#666;\npadding:0 10px;\nfont-size:14px;\nborder-bottom:1px solid #e5e5e5;\nbackground:#f7f7f7;\nborder-radius:4px 4px 0 0;\n}\n\n.easyDialog_wrapper .close_btn{\nfont-family:arial;\nfont-size:18px;\n_font-size:12px;\nfont-weight:700;\ncolor:#999;\ntext-decoration:none;\nfloat:right;\n}\n\n.easyDialog_wrapper .close_btn:hover{\ncolor:#333;\n}\n\n.easyDialog_wrapper .easyDialog_text{\npadding:25px 10px;\nfont-size:13px;\nline-height:22px;\n}\n\n.easyDialog_wrapper .easyDialog_footer{\npadding:0 10px;\n*zoom:1;\n}\n\n.easyDialog_wrapper .easyDialog_footer:after{\ncontent:\'\';\ndisplay:block;\nheight:0;\noverflow:hidden;\nvisibility:hidden;\nclear:both;\n}\n\n.easyDialog_wrapper .btn_highlight,\n.easyDialog_wrapper .btn_normal{\nborder:1px solid;\nborder-radius:2px;\ncursor:pointer;\nfont-family:"Microsoft yahei", Arial;\nfloat:right;\nfont-size:12px;\npadding:0 12px;\nheight:24px;\nline-height:24px;\nmargin-bottom:10px;\n}\n\n.easyDialog_wrapper .btn_highlight{\nbackground:#4787ed;\nbackground:-webkit-gradient(linear,center bottom,center top,from(#4787ed),to(#4d90fe));\nbackground:-moz-linear-gradient(90deg, #4787ed, #4d90fe);\nborder-color:#3079ed;\ncolor:#fff;\n}\n\n.easyDialog_wrapper .btn_normal{\nmargin-left:10px;\nborder-color:#c3c3c3;\nbackground:#ececec;\ncolor:#333;\nbackground:-webkit-gradient(linear,center bottom,center top,from(#ececec),to(#f4f4f4));\nbackground:-moz-linear-gradient(90deg,#ececec,#f4f4f4);\n}\n\n#alarmHead{\n font-size:120%;\n }';
  763.  
  764. cssContainer = document.createElement("style");
  765.  
  766. cssContainer.type = "text/css";
  767.  
  768. cssContainer.textContent = cssContent;
  769.  
  770. try {
  771. document.getElementsByTagName("head")[0].appendChild(cssContainer);
  772. } catch (_error) {
  773. error = _error;
  774. console.log(error);
  775. }
  776.  
  777. }).call(this);
  778.  
  779. (function() {
  780. var alarmBox, cancelClock, configBox, dateToWork, divToAppend, fridgeMagnet, img64, longWords, msToCoolDown, msToWork, pulseCount, setClock, startPulse, timeoutAlarm;
  781.  
  782. img64 = 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90HFg8CKzDwrf0AAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAA1VJREFUOMudlE9o22UYxz/P82bJ0jZU1M60TZtDVWbA+ifpiCnSoZYJEw87qAdRmH8OguguguBhpx7Eo0wPXkVhuwkWO3EoNkkbOqW0VdYq2LRdalxobYvNn9/v8ZJJbdox/d5eeJ4P7/v9Pu8jtErS6fSAqo6qagroA44AmNk68KOZTW1vb0/Pzs7utDTvPWQymQjwrIicFZE0UAVKwB+AB3QAx4CamY2b2cVqtZqfmZmptwAHBwfbOzo63lDV182sU0Qu+74/3mg0rppZRVXN87xgMBi8V0SeFpEXzOy653kX6vX6ZzehAYBkMnkkFAq9pKrvAL8BH1Sr1UuFQuHGAZYU4/F4LhaLzYvIm8658865XeAiYA4gHo8/5px718x+9zzvfC6Xu7S2trbDIdrc3GwUi8W5/v7+deAJETne3d19dXV1dV0TiURQVZ8BjpnZhXw+P9H0q8XjffIqlcqXwOfAw4FA4DSgbmBg4CFVPQdc2djY+LBcLlebAfXEYrFTXV1dG6VSaesgYrlc9qLR6JpzLiMiD0Sj0ayq6gkRiQKXFxYWtv9JS6RLVV8Oh8Njw8PDJ5sT0KJCobBoZleA+wOBwCMKHAe2arXa/N7CycnJOTMbAxrAGHAunU7feQDTzGwKaIjIfaqq/Wa2Cfy536NsNpur1+vvmdmkqo465/oONNPzfgX+MrOoNmeuTUQChzypJCITZrbNbUhF5AZwh4jcc1iRiNRuBWne/KiIlNTMrgERVU3yP6WqjwLOzBYVmAV8EXkymUze/V9hqVSqDxgxs8VGo/GDep43aWbfqepT4XD4OcDdLiyRSARDodArInIC+GJ6evpnzefzq77vfwpUgLOZTObUyMhIYF+KdWDH8zx/L6yzs/OMiLwIzJvZOOA7gPb29mI4HBYROS0iQ2YW7unpWV5ZWdkC6O3trTnnrqvqT8vLy7upVKqvra3tLefc20DI87z3c7nc14D9a31FIpHXRORVIGZm35vZN8Bco9FYuZlmM4AREXkcKJrZR+vr6x8vLS1VWz5/MplsC4VCJ4HnRWRURCLADrDbLDkKRMxMgW993/9ERL7KZrNb3GqbDA0N3RUIBNIikhaRB0WkF4gAFd/3rwFTwEQ2m/1lf+/fxPttOfXp2toAAAAASUVORK5CYII=';
  783.  
  784. fridgeMagnet = '<div class="pinned">\n <img id="boxOpen" src="data:image/png;base64,' + img64 + '"></div>';
  785.  
  786. alarmBox = '<div id="alarmBox">\n <div id="alarmHead">工作时间到</div>\n <div id="hiddenExit">\n <div>请输入以下文字:</div>\n <canvas id="captcha" width="400" height="30"></canvas><br>\n <input type="text" name="captchaIn" id="captchaIn" size="30" style="width:80%;">\n </div>\n</div>';
  787.  
  788. configBox = '<div id="configBox">\n <div>我就休息\n <input type="number" name="minuteField" id="minuteField" min="1" size="3"\n value="1" onkeyup="this.value=this.value.replace(/[^0-9.]/g,\'\')" />\n 分钟</div>\n</div>';
  789.  
  790. dateToWork = 0;
  791.  
  792. pulseCount = 0;
  793.  
  794. msToCoolDown = 1 * 3600 * 1000;
  795.  
  796. longWords = ["我荒废的今日,正是昨日殒身之人祈求的明日", "Procrastination", "拖延的基础实际上是对自身不切实际的期望", "the longer you wait the worse it gets", "停止空谈,开始行动", "Lorem ipsum dolor sit amet.", "吃葡萄不吐葡萄皮不吃葡萄倒吐葡萄皮"];
  797.  
  798. '不要超过20个中文字符\n上面都是我编的,我编不下去了';
  799.  
  800. setClock = function() {
  801. '设置休息时间并开始计时';
  802. var dateClicking, msToWork, relaxMinutes;
  803. relaxMinutes = parseFloat(document.getElementById("minuteField").value);
  804. console.log(relaxMinutes);
  805. if (isNaN(relaxMinutes)) {
  806. return false;
  807. }
  808. if (relaxMinutes <= 0) {
  809. return false;
  810. }
  811. dateClicking = new Date();
  812. dateToWork = new Date();
  813. msToWork = dateClicking.getTime() + relaxMinutes * 60 * 1000;
  814. dateToWork.setTime(parseInt(msToWork));
  815. localStorage.setItem("msToWork", msToWork);
  816. console.log(dateClicking);
  817. console.log(dateToWork);
  818. console.log(msToWork);
  819. startPulse();
  820. return true;
  821. };
  822.  
  823. startPulse = function() {
  824. '每秒检测一次,频率可以更低\n我不喜欢轮询……有更好的方法吗?';
  825. var dateNow;
  826. clearTimeout(pulseCount);
  827. dateNow = new Date();
  828. if (dateNow >= dateToWork) {
  829. return timeoutAlarm();
  830. } else {
  831. return pulseCount = setTimeout(startPulse, 1000);
  832. }
  833. };
  834.  
  835. cancelClock = function() {
  836. '干掉pulse,删掉storage';
  837. clearTimeout(pulseCount);
  838. localStorage.removeItem("msToWork");
  839. try {
  840. easyDialog.close();
  841. } catch (_error) {}
  842. return true;
  843. };
  844.  
  845. timeoutAlarm = function() {
  846. '......';
  847. var c, cxt, longWord;
  848. longWord = longWords[Math.floor(Math.random() * longWords.length)];
  849. setTimeout(cancelClock, msToCoolDown);
  850. easyDialog.open({
  851. container: {
  852. content: alarmBox,
  853. yesFn: function() {
  854. if (longWord === document.getElementById("captchaIn").value) {
  855. return cancelClock();
  856. } else {
  857. return false;
  858. }
  859. }
  860. }
  861. });
  862. c = document.getElementById("captcha");
  863. cxt = c.getContext("2d");
  864. cxt.font = "20px serif";
  865. cxt.textBaseline = "top";
  866. return cxt.fillText(longWord, 0, 5, 600);
  867. };
  868.  
  869. divToAppend = document.createElement("div");
  870.  
  871. divToAppend.innerHTML = fridgeMagnet;
  872.  
  873. document.body.appendChild(divToAppend);
  874.  
  875. document.getElementById("boxOpen").onclick = function() {
  876. return easyDialog.open({
  877. container: {
  878. content: configBox,
  879. yesFn: setClock,
  880. noFn: true,
  881. yesText: "真的!",
  882. noText: "逗你玩"
  883. }
  884. });
  885. };
  886.  
  887. '页面间通信';
  888.  
  889. window.addEventListener("storage", function(e) {
  890. console.log(e);
  891. if (e.key === "msToWork") {
  892. if (e.newValue != null) {
  893. dateToWork = new Date();
  894. dateToWork.setTime(parseInt(e.newValue));
  895. startPulse();
  896. } else {
  897. cancelClock();
  898. }
  899. }
  900. }, false);
  901.  
  902. '页面加载时检测';
  903.  
  904. if ((msToWork = localStorage.getItem("msToWork")) != null) {
  905. console.log(msToWork);
  906. dateToWork = new Date();
  907. dateToWork.setTime(parseInt(msToWork));
  908. startPulse();
  909. }
  910.  
  911. /*
  912. TODO
  913. # 用GM-keys实现部分跨域
  914. # 取消已开始的计时
  915. # 树形菜单设置界面
  916. # 改变颜色
  917. # 让图标变得可动!!!
  918. DONE
  919. # 多标签状态共享
  920. # 本地存储
  921. # 有趣的关闭手段
  922. */
  923.  
  924.  
  925. }).call(this);