all.js 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507
  1. /* ---- plugins/Sidebar/media/Class.coffee ---- */
  2. (function() {
  3. var Class,
  4. slice = [].slice;
  5. Class = (function() {
  6. function Class() {}
  7. Class.prototype.trace = true;
  8. Class.prototype.log = function() {
  9. var args;
  10. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  11. if (!this.trace) {
  12. return;
  13. }
  14. if (typeof console === 'undefined') {
  15. return;
  16. }
  17. args.unshift("[" + this.constructor.name + "]");
  18. console.log.apply(console, args);
  19. return this;
  20. };
  21. Class.prototype.logStart = function() {
  22. var args, name;
  23. name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
  24. if (!this.trace) {
  25. return;
  26. }
  27. this.logtimers || (this.logtimers = {});
  28. this.logtimers[name] = +(new Date);
  29. if (args.length > 0) {
  30. this.log.apply(this, ["" + name].concat(slice.call(args), ["(started)"]));
  31. }
  32. return this;
  33. };
  34. Class.prototype.logEnd = function() {
  35. var args, ms, name;
  36. name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
  37. ms = +(new Date) - this.logtimers[name];
  38. this.log.apply(this, ["" + name].concat(slice.call(args), ["(Done in " + ms + "ms)"]));
  39. return this;
  40. };
  41. return Class;
  42. })();
  43. window.Class = Class;
  44. }).call(this);
  45. /* ---- plugins/Sidebar/media/Internals.coffee ---- */
  46. (function() {
  47. var Internals,
  48. bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
  49. extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  50. hasProp = {}.hasOwnProperty;
  51. Internals = (function(superClass) {
  52. extend(Internals, superClass);
  53. function Internals(sidebar) {
  54. this.sidebar = sidebar;
  55. this.stopDragY = bind(this.stopDragY, this);
  56. this.onClosed = bind(this.onClosed, this);
  57. this.onOpened = bind(this.onOpened, this);
  58. this.open = bind(this.open, this);
  59. this.tag = null;
  60. this.opened = false;
  61. if (window.top.location.hash === "#internals") {
  62. setTimeout(((function(_this) {
  63. return function() {
  64. return _this.open();
  65. };
  66. })(this)), 10);
  67. }
  68. }
  69. Internals.prototype.createHtmltag = function() {
  70. this.when_loaded = $.Deferred();
  71. if (!this.container) {
  72. this.container = $("<div class=\"internals-container\">\n <div class=\"internals\"><div class=\"internals-middle\">\n <div class=\"mynode\"></div>\n <div class=\"peers\">\n <div class=\"peer\"><div class=\"line\"></div><a href=\"#\" class=\"icon\">\u25BD</div></div>\n </div>\n </div></div>\n</div>");
  73. this.container.appendTo(document.body);
  74. return this.tag = this.container.find(".internals");
  75. }
  76. };
  77. Internals.prototype.open = function() {
  78. this.createHtmltag();
  79. this.sidebar.fixbutton_targety = this.sidebar.page_height;
  80. return this.stopDragY();
  81. };
  82. Internals.prototype.onOpened = function() {
  83. this.sidebar.onClosed();
  84. return this.log("onOpened");
  85. };
  86. Internals.prototype.onClosed = function() {
  87. return $(document.body).removeClass("body-internals");
  88. };
  89. Internals.prototype.stopDragY = function() {
  90. var targety;
  91. if (this.sidebar.fixbutton_targety === this.sidebar.fixbutton_inity) {
  92. targety = 0;
  93. this.opened = false;
  94. } else {
  95. targety = this.sidebar.fixbutton_targety - this.sidebar.fixbutton_inity;
  96. this.onOpened();
  97. this.opened = true;
  98. }
  99. if (this.tag) {
  100. this.tag.css("transition", "0.5s ease-out");
  101. this.tag.css("transform", "translateY(" + targety + "px)").one(transitionEnd, (function(_this) {
  102. return function() {
  103. _this.tag.css("transition", "");
  104. if (!_this.opened) {
  105. return _this.log("cleanup");
  106. }
  107. };
  108. })(this));
  109. }
  110. this.log("stopdrag", "opened:", this.opened, targety);
  111. if (!this.opened) {
  112. return this.onClosed();
  113. }
  114. };
  115. return Internals;
  116. })(Class);
  117. window.Internals = Internals;
  118. }).call(this);
  119. /* ---- plugins/Sidebar/media/Menu.coffee ---- */
  120. (function() {
  121. var Menu,
  122. slice = [].slice;
  123. Menu = (function() {
  124. function Menu(button) {
  125. this.button = button;
  126. this.elem = $(".menu.template").clone().removeClass("template");
  127. this.elem.appendTo("body");
  128. this.items = [];
  129. }
  130. Menu.prototype.show = function() {
  131. var button_pos, left;
  132. if (window.visible_menu && window.visible_menu.button[0] === this.button[0]) {
  133. window.visible_menu.hide();
  134. return this.hide();
  135. } else {
  136. button_pos = this.button.offset();
  137. left = button_pos.left;
  138. this.elem.css({
  139. "top": button_pos.top + this.button.outerHeight(),
  140. "left": left
  141. });
  142. this.button.addClass("menu-active");
  143. this.elem.addClass("visible");
  144. if (this.elem.position().left + this.elem.width() + 20 > window.innerWidth) {
  145. this.elem.css("left", window.innerWidth - this.elem.width() - 20);
  146. }
  147. if (window.visible_menu) {
  148. window.visible_menu.hide();
  149. }
  150. return window.visible_menu = this;
  151. }
  152. };
  153. Menu.prototype.hide = function() {
  154. this.elem.removeClass("visible");
  155. this.button.removeClass("menu-active");
  156. return window.visible_menu = null;
  157. };
  158. Menu.prototype.addItem = function(title, cb) {
  159. var item;
  160. item = $(".menu-item.template", this.elem).clone().removeClass("template");
  161. item.html(title);
  162. item.on("click", (function(_this) {
  163. return function() {
  164. if (!cb(item)) {
  165. _this.hide();
  166. }
  167. return false;
  168. };
  169. })(this));
  170. item.appendTo(this.elem);
  171. this.items.push(item);
  172. return item;
  173. };
  174. Menu.prototype.log = function() {
  175. var args;
  176. args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
  177. return console.log.apply(console, ["[Menu]"].concat(slice.call(args)));
  178. };
  179. return Menu;
  180. })();
  181. window.Menu = Menu;
  182. $("body").on("click", function(e) {
  183. if (window.visible_menu && e.target !== window.visible_menu.button[0] && $(e.target).parent()[0] !== window.visible_menu.elem[0]) {
  184. return window.visible_menu.hide();
  185. }
  186. });
  187. }).call(this);
  188. /* ---- plugins/Sidebar/media/RateLimit.coffee ---- */
  189. (function() {
  190. var call_after_interval, limits;
  191. limits = {};
  192. call_after_interval = {};
  193. window.RateLimit = function(interval, fn) {
  194. if (!limits[fn]) {
  195. call_after_interval[fn] = false;
  196. fn();
  197. return limits[fn] = setTimeout((function() {
  198. if (call_after_interval[fn]) {
  199. fn();
  200. }
  201. delete limits[fn];
  202. return delete call_after_interval[fn];
  203. }), interval);
  204. } else {
  205. return call_after_interval[fn] = true;
  206. }
  207. };
  208. }).call(this);
  209. /* ---- plugins/Sidebar/media/Scrollable.js ---- */
  210. /* via http://jsfiddle.net/elGrecode/00dgurnn/ */
  211. window.initScrollable = function () {
  212. var scrollContainer = document.querySelector('.scrollable'),
  213. scrollContentWrapper = document.querySelector('.scrollable .content-wrapper'),
  214. scrollContent = document.querySelector('.scrollable .content'),
  215. contentPosition = 0,
  216. scrollerBeingDragged = false,
  217. scroller,
  218. topPosition,
  219. scrollerHeight;
  220. function calculateScrollerHeight() {
  221. // *Calculation of how tall scroller should be
  222. var visibleRatio = scrollContainer.offsetHeight / scrollContentWrapper.scrollHeight;
  223. if (visibleRatio == 1)
  224. scroller.style.display = "none";
  225. else
  226. scroller.style.display = "block";
  227. return visibleRatio * scrollContainer.offsetHeight;
  228. }
  229. function moveScroller(evt) {
  230. // Move Scroll bar to top offset
  231. var scrollPercentage = evt.target.scrollTop / scrollContentWrapper.scrollHeight;
  232. topPosition = scrollPercentage * (scrollContainer.offsetHeight - 5); // 5px arbitrary offset so scroll bar doesn't move too far beyond content wrapper bounding box
  233. scroller.style.top = topPosition + 'px';
  234. }
  235. function startDrag(evt) {
  236. normalizedPosition = evt.pageY;
  237. contentPosition = scrollContentWrapper.scrollTop;
  238. scrollerBeingDragged = true;
  239. window.addEventListener('mousemove', scrollBarScroll);
  240. return false;
  241. }
  242. function stopDrag(evt) {
  243. scrollerBeingDragged = false;
  244. window.removeEventListener('mousemove', scrollBarScroll);
  245. }
  246. function scrollBarScroll(evt) {
  247. if (scrollerBeingDragged === true) {
  248. evt.preventDefault();
  249. var mouseDifferential = evt.pageY - normalizedPosition;
  250. var scrollEquivalent = mouseDifferential * (scrollContentWrapper.scrollHeight / scrollContainer.offsetHeight);
  251. scrollContentWrapper.scrollTop = contentPosition + scrollEquivalent;
  252. }
  253. }
  254. function updateHeight() {
  255. scrollerHeight = calculateScrollerHeight() - 10;
  256. scroller.style.height = scrollerHeight + 'px';
  257. }
  258. function createScroller() {
  259. // *Creates scroller element and appends to '.scrollable' div
  260. // create scroller element
  261. scroller = document.createElement("div");
  262. scroller.className = 'scroller';
  263. // determine how big scroller should be based on content
  264. scrollerHeight = calculateScrollerHeight() - 10;
  265. if (scrollerHeight / scrollContainer.offsetHeight < 1) {
  266. // *If there is a need to have scroll bar based on content size
  267. scroller.style.height = scrollerHeight + 'px';
  268. // append scroller to scrollContainer div
  269. scrollContainer.appendChild(scroller);
  270. // show scroll path divot
  271. scrollContainer.className += ' showScroll';
  272. // attach related draggable listeners
  273. scroller.addEventListener('mousedown', startDrag);
  274. window.addEventListener('mouseup', stopDrag);
  275. }
  276. }
  277. createScroller();
  278. // *** Listeners ***
  279. scrollContentWrapper.addEventListener('scroll', moveScroller);
  280. return updateHeight;
  281. };
  282. /* ---- plugins/Sidebar/media/Sidebar.coffee ---- */
  283. (function() {
  284. var Sidebar, wrapper,
  285. bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
  286. extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  287. hasProp = {}.hasOwnProperty,
  288. indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
  289. Sidebar = (function(superClass) {
  290. extend(Sidebar, superClass);
  291. function Sidebar(wrapper1) {
  292. this.wrapper = wrapper1;
  293. this.unloadGlobe = bind(this.unloadGlobe, this);
  294. this.displayGlobe = bind(this.displayGlobe, this);
  295. this.loadGlobe = bind(this.loadGlobe, this);
  296. this.animDrag = bind(this.animDrag, this);
  297. this.setHtmlTag = bind(this.setHtmlTag, this);
  298. this.waitMove = bind(this.waitMove, this);
  299. this.resized = bind(this.resized, this);
  300. this.tag = null;
  301. this.container = null;
  302. this.opened = false;
  303. this.width = 410;
  304. this.internals = new Internals(this);
  305. this.fixbutton = $(".fixbutton");
  306. this.fixbutton_addx = 0;
  307. this.fixbutton_addy = 0;
  308. this.fixbutton_initx = 0;
  309. this.fixbutton_inity = 15;
  310. this.fixbutton_targetx = 0;
  311. this.move_lock = null;
  312. this.page_width = $(window).width();
  313. this.page_height = $(window).height();
  314. this.frame = $("#inner-iframe");
  315. this.initFixbutton();
  316. this.dragStarted = 0;
  317. this.globe = null;
  318. this.preload_html = null;
  319. this.original_set_site_info = this.wrapper.setSiteInfo;
  320. if (false) {
  321. this.startDrag();
  322. this.moved();
  323. this.fixbutton_targetx = this.fixbutton_initx - this.width;
  324. this.stopDrag();
  325. }
  326. }
  327. Sidebar.prototype.initFixbutton = function() {
  328. this.fixbutton.on("mousedown touchstart", (function(_this) {
  329. return function(e) {
  330. if (e.button > 0) {
  331. return;
  332. }
  333. e.preventDefault();
  334. _this.fixbutton.off("click touchend touchcancel");
  335. _this.fixbutton.off("mousemove touchmove");
  336. _this.dragStarted = +(new Date);
  337. return _this.fixbutton.one("mousemove touchmove", function(e) {
  338. var mousex, mousey;
  339. mousex = e.pageX;
  340. mousey = e.pageY;
  341. if (!mousex) {
  342. mousex = e.originalEvent.touches[0].pageX;
  343. mousey = e.originalEvent.touches[0].pageY;
  344. }
  345. _this.fixbutton_addx = _this.fixbutton.offset().left - mousex;
  346. _this.fixbutton_addy = _this.fixbutton.offset().top - mousey;
  347. return _this.startDrag();
  348. });
  349. };
  350. })(this));
  351. this.fixbutton.parent().on("click touchend touchcancel", (function(_this) {
  352. return function(e) {
  353. if ((+(new Date)) - _this.dragStarted < 100) {
  354. window.top.location = _this.fixbutton.find(".fixbutton-bg").attr("href");
  355. }
  356. return _this.stopDrag();
  357. };
  358. })(this));
  359. this.resized();
  360. return $(window).on("resize", this.resized);
  361. };
  362. Sidebar.prototype.resized = function() {
  363. this.page_width = $(window).width();
  364. this.page_height = $(window).height();
  365. this.fixbutton_initx = this.page_width - 75;
  366. if (this.opened) {
  367. return this.fixbutton.css({
  368. left: this.fixbutton_initx - this.width
  369. });
  370. } else {
  371. return this.fixbutton.css({
  372. left: this.fixbutton_initx
  373. });
  374. }
  375. };
  376. Sidebar.prototype.startDrag = function() {
  377. this.move_lock = "x";
  378. this.log("startDrag");
  379. this.fixbutton_targetx = this.fixbutton_initx;
  380. this.fixbutton.addClass("dragging");
  381. $("<div class='drag-bg'></div>").appendTo(document.body);
  382. if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) {
  383. this.fixbutton.css("pointer-events", "none");
  384. }
  385. this.fixbutton.one("click", (function(_this) {
  386. return function(e) {
  387. var moved_x, moved_y;
  388. _this.stopDrag();
  389. _this.fixbutton.removeClass("dragging");
  390. moved_x = Math.abs(_this.fixbutton.offset().left - _this.fixbutton_initx);
  391. moved_y = Math.abs(_this.fixbutton.offset().top - _this.fixbutton_inity);
  392. if (moved_x > 5 || moved_y > 10) {
  393. return e.preventDefault();
  394. }
  395. };
  396. })(this));
  397. this.fixbutton.parents().on("mousemove touchmove", this.animDrag);
  398. this.fixbutton.parents().on("mousemove touchmove", this.waitMove);
  399. return this.fixbutton.parents().one("mouseup touchend touchcancel", (function(_this) {
  400. return function(e) {
  401. e.preventDefault();
  402. return _this.stopDrag();
  403. };
  404. })(this));
  405. };
  406. Sidebar.prototype.waitMove = function(e) {
  407. var moved_x, moved_y;
  408. document.body.style.perspective = "1000px";
  409. document.body.style.height = "100%";
  410. document.body.style.willChange = "perspective";
  411. document.documentElement.style.height = "100%";
  412. moved_x = Math.abs(parseInt(this.fixbutton[0].style.left) - this.fixbutton_targetx);
  413. moved_y = Math.abs(parseInt(this.fixbutton[0].style.top) - this.fixbutton_targety);
  414. if (moved_x > 5 && (+(new Date)) - this.dragStarted + moved_x > 50) {
  415. this.moved("x");
  416. this.fixbutton.stop().animate({
  417. "top": this.fixbutton_inity
  418. }, 1000);
  419. return this.fixbutton.parents().off("mousemove touchmove", this.waitMove);
  420. } else if (moved_y > 5 && (+(new Date)) - this.dragStarted + moved_y > 50) {
  421. this.moved("y");
  422. return this.fixbutton.parents().off("mousemove touchmove", this.waitMove);
  423. }
  424. };
  425. Sidebar.prototype.moved = function(direction) {
  426. var img;
  427. this.log("Moved", direction);
  428. this.move_lock = direction;
  429. if (direction === "y") {
  430. $(document.body).addClass("body-internals");
  431. return this.internals.createHtmltag();
  432. }
  433. this.createHtmltag();
  434. $(document.body).addClass("body-sidebar");
  435. this.container.on("mousedown touchend touchcancel", (function(_this) {
  436. return function(e) {
  437. if (e.target !== e.currentTarget) {
  438. return true;
  439. }
  440. _this.log("closing");
  441. if ($(document.body).hasClass("body-sidebar")) {
  442. _this.close();
  443. return true;
  444. }
  445. };
  446. })(this));
  447. $(window).off("resize");
  448. $(window).on("resize", (function(_this) {
  449. return function() {
  450. $(document.body).css("height", $(window).height());
  451. _this.scrollable();
  452. return _this.resized();
  453. };
  454. })(this));
  455. this.wrapper.setSiteInfo = (function(_this) {
  456. return function(site_info) {
  457. _this.setSiteInfo(site_info);
  458. return _this.original_set_site_info.apply(_this.wrapper, arguments);
  459. };
  460. })(this);
  461. img = new Image();
  462. return img.src = "/uimedia/globe/world.jpg";
  463. };
  464. Sidebar.prototype.setSiteInfo = function(site_info) {
  465. RateLimit(1500, (function(_this) {
  466. return function() {
  467. return _this.updateHtmlTag();
  468. };
  469. })(this));
  470. return RateLimit(30000, (function(_this) {
  471. return function() {
  472. return _this.displayGlobe();
  473. };
  474. })(this));
  475. };
  476. Sidebar.prototype.createHtmltag = function() {
  477. this.when_loaded = $.Deferred();
  478. if (!this.container) {
  479. this.container = $("<div class=\"sidebar-container\"><div class=\"sidebar scrollable\"><div class=\"content-wrapper\"><div class=\"content\">\n</div></div></div></div>");
  480. this.container.appendTo(document.body);
  481. this.tag = this.container.find(".sidebar");
  482. this.updateHtmlTag();
  483. return this.scrollable = window.initScrollable();
  484. }
  485. };
  486. Sidebar.prototype.updateHtmlTag = function() {
  487. if (this.preload_html) {
  488. this.setHtmlTag(this.preload_html);
  489. return this.preload_html = null;
  490. } else {
  491. return this.wrapper.ws.cmd("sidebarGetHtmlTag", {}, this.setHtmlTag);
  492. }
  493. };
  494. Sidebar.prototype.setHtmlTag = function(res) {
  495. if (this.tag.find(".content").children().length === 0) {
  496. this.log("Creating content");
  497. this.container.addClass("loaded");
  498. morphdom(this.tag.find(".content")[0], '<div class="content">' + res + '</div>');
  499. this.when_loaded.resolve();
  500. } else {
  501. morphdom(this.tag.find(".content")[0], '<div class="content">' + res + '</div>', {
  502. onBeforeMorphEl: function(from_el, to_el) {
  503. if (from_el.className === "globe" || from_el.className.indexOf("noupdate") >= 0) {
  504. return false;
  505. } else {
  506. return true;
  507. }
  508. }
  509. });
  510. }
  511. this.tag.find("#privatekey-add").off("click, touchend").on("click touchend", (function(_this) {
  512. return function(e) {
  513. _this.wrapper.displayPrompt("Enter your private key:", "password", "Save", "", function(privatekey) {
  514. return _this.wrapper.ws.cmd("userSetSitePrivatekey", [privatekey], function(res) {
  515. return _this.wrapper.notifications.add("privatekey", "done", "Private key saved for site signing", 5000);
  516. });
  517. });
  518. return false;
  519. };
  520. })(this));
  521. return this.tag.find("#privatekey-forgot").off("click, touchend").on("click touchend", (function(_this) {
  522. return function(e) {
  523. _this.wrapper.displayConfirm("Remove saved private key for this site?", "Forgot", function(res) {
  524. if (!res) {
  525. return false;
  526. }
  527. return _this.wrapper.ws.cmd("userSetSitePrivatekey", [""], function(res) {
  528. return _this.wrapper.notifications.add("privatekey", "done", "Saved private key removed", 5000);
  529. });
  530. });
  531. return false;
  532. };
  533. })(this));
  534. };
  535. Sidebar.prototype.animDrag = function(e) {
  536. var mousex, mousey, overdrag, overdrag_percent, targetx, targety;
  537. mousex = e.pageX;
  538. mousey = e.pageY;
  539. if (!mousex && e.originalEvent.touches) {
  540. mousex = e.originalEvent.touches[0].pageX;
  541. mousey = e.originalEvent.touches[0].pageY;
  542. }
  543. overdrag = this.fixbutton_initx - this.width - mousex;
  544. if (overdrag > 0) {
  545. overdrag_percent = 1 + overdrag / 300;
  546. mousex = (mousex + (this.fixbutton_initx - this.width) * overdrag_percent) / (1 + overdrag_percent);
  547. }
  548. targetx = this.fixbutton_initx - mousex - this.fixbutton_addx;
  549. targety = this.fixbutton_inity - mousey - this.fixbutton_addy;
  550. if (this.move_lock === "x") {
  551. targety = this.fixbutton_inity;
  552. } else if (this.move_lock === "y") {
  553. targetx = this.fixbutton_initx;
  554. }
  555. if (!this.move_lock || this.move_lock === "x") {
  556. this.fixbutton[0].style.left = (mousex + this.fixbutton_addx) + "px";
  557. if (this.tag) {
  558. this.tag[0].style.transform = "translateX(" + (0 - targetx) + "px)";
  559. }
  560. }
  561. if (!this.move_lock || this.move_lock === "y") {
  562. this.fixbutton[0].style.top = (mousey + this.fixbutton_addy) + "px";
  563. if (this.internals.tag) {
  564. this.internals.tag[0].style.transform = "translateY(" + (0 - targety) + "px)";
  565. }
  566. }
  567. if ((!this.opened && targetx > this.width / 3) || (this.opened && targetx > this.width * 0.9)) {
  568. this.fixbutton_targetx = this.fixbutton_initx - this.width;
  569. } else {
  570. this.fixbutton_targetx = this.fixbutton_initx;
  571. }
  572. if ((!this.internals.opened && 0 - targety > this.page_height / 10) || (this.internals.opened && 0 - targety > this.page_height * 0.95)) {
  573. return this.fixbutton_targety = this.page_height - this.fixbutton_inity - 50;
  574. } else {
  575. return this.fixbutton_targety = this.fixbutton_inity;
  576. }
  577. };
  578. Sidebar.prototype.stopDrag = function() {
  579. var left, top;
  580. this.fixbutton.parents().off("mousemove touchmove");
  581. this.fixbutton.off("mousemove touchmove");
  582. this.fixbutton.css("pointer-events", "");
  583. $(".drag-bg").remove();
  584. if (!this.fixbutton.hasClass("dragging")) {
  585. return;
  586. }
  587. this.fixbutton.removeClass("dragging");
  588. if (this.fixbutton_targetx !== this.fixbutton.offset().left) {
  589. if (this.move_lock === "y") {
  590. top = this.fixbutton_targety;
  591. left = this.fixbutton_initx;
  592. }
  593. if (this.move_lock === "x") {
  594. top = this.fixbutton_inity;
  595. left = this.fixbutton_targetx;
  596. }
  597. this.fixbutton.stop().animate({
  598. "left": left,
  599. "top": top
  600. }, 500, "easeOutBack", (function(_this) {
  601. return function() {
  602. if (_this.fixbutton_targetx === _this.fixbutton_initx) {
  603. _this.fixbutton.css("left", "auto");
  604. } else {
  605. _this.fixbutton.css("left", left);
  606. }
  607. return $(".fixbutton-bg").trigger("mouseout");
  608. };
  609. })(this));
  610. this.stopDragX();
  611. this.internals.stopDragY();
  612. }
  613. return this.move_lock = null;
  614. };
  615. Sidebar.prototype.stopDragX = function() {
  616. var targetx;
  617. if (this.fixbutton_targetx === this.fixbutton_initx || this.move_lock === "y") {
  618. targetx = 0;
  619. this.opened = false;
  620. } else {
  621. targetx = this.width;
  622. if (this.opened) {
  623. this.onOpened();
  624. } else {
  625. this.when_loaded.done((function(_this) {
  626. return function() {
  627. return _this.onOpened();
  628. };
  629. })(this));
  630. }
  631. this.opened = true;
  632. }
  633. if (this.tag) {
  634. this.tag.css("transition", "0.4s ease-out");
  635. this.tag.css("transform", "translateX(-" + targetx + "px)").one(transitionEnd, (function(_this) {
  636. return function() {
  637. _this.tag.css("transition", "");
  638. if (!_this.opened) {
  639. _this.container.remove();
  640. _this.container = null;
  641. if (_this.tag) {
  642. _this.tag.remove();
  643. return _this.tag = null;
  644. }
  645. }
  646. };
  647. })(this));
  648. }
  649. this.log("stopdrag", "opened:", this.opened);
  650. if (!this.opened) {
  651. return this.onClosed();
  652. }
  653. };
  654. Sidebar.prototype.onOpened = function() {
  655. var menu;
  656. this.log("Opened");
  657. this.scrollable();
  658. this.tag.find("#checkbox-owned, #checkbox-autodownloadoptional").off("click touchend").on("click touchend", (function(_this) {
  659. return function() {
  660. return setTimeout((function() {
  661. return _this.scrollable();
  662. }), 300);
  663. };
  664. })(this));
  665. this.tag.find("#button-sitelimit").off("click touchend").on("click touchend", (function(_this) {
  666. return function() {
  667. _this.wrapper.ws.cmd("siteSetLimit", $("#input-sitelimit").val(), function(res) {
  668. if (res === "ok") {
  669. _this.wrapper.notifications.add("done-sitelimit", "done", "Site storage limit modified!", 5000);
  670. }
  671. return _this.updateHtmlTag();
  672. });
  673. return false;
  674. };
  675. })(this));
  676. this.tag.find("#button-autodownload_bigfile_size_limit").off("click touchend").on("click touchend", (function(_this) {
  677. return function() {
  678. _this.wrapper.ws.cmd("siteSetAutodownloadBigfileLimit", $("#input-autodownload_bigfile_size_limit").val(), function(res) {
  679. if (res === "ok") {
  680. _this.wrapper.notifications.add("done-bigfilelimit", "done", "Site bigfile auto download limit modified!", 5000);
  681. }
  682. return _this.updateHtmlTag();
  683. });
  684. return false;
  685. };
  686. })(this));
  687. this.tag.find("#button-dbreload").off("click touchend").on("click touchend", (function(_this) {
  688. return function() {
  689. _this.wrapper.ws.cmd("dbReload", [], function() {
  690. _this.wrapper.notifications.add("done-dbreload", "done", "Database schema reloaded!", 5000);
  691. return _this.updateHtmlTag();
  692. });
  693. return false;
  694. };
  695. })(this));
  696. this.tag.find("#button-dbrebuild").off("click touchend").on("click touchend", (function(_this) {
  697. return function() {
  698. _this.wrapper.notifications.add("done-dbrebuild", "info", "Database rebuilding....");
  699. _this.wrapper.ws.cmd("dbRebuild", [], function() {
  700. _this.wrapper.notifications.add("done-dbrebuild", "done", "Database rebuilt!", 5000);
  701. return _this.updateHtmlTag();
  702. });
  703. return false;
  704. };
  705. })(this));
  706. this.tag.find("#button-update").off("click touchend").on("click touchend", (function(_this) {
  707. return function() {
  708. _this.tag.find("#button-update").addClass("loading");
  709. _this.wrapper.ws.cmd("siteUpdate", _this.wrapper.site_info.address, function() {
  710. _this.wrapper.notifications.add("done-updated", "done", "Site updated!", 5000);
  711. return _this.tag.find("#button-update").removeClass("loading");
  712. });
  713. return false;
  714. };
  715. })(this));
  716. this.tag.find("#button-pause").off("click touchend").on("click touchend", (function(_this) {
  717. return function() {
  718. _this.tag.find("#button-pause").addClass("hidden");
  719. _this.wrapper.ws.cmd("sitePause", _this.wrapper.site_info.address);
  720. return false;
  721. };
  722. })(this));
  723. this.tag.find("#button-resume").off("click touchend").on("click touchend", (function(_this) {
  724. return function() {
  725. _this.tag.find("#button-resume").addClass("hidden");
  726. _this.wrapper.ws.cmd("siteResume", _this.wrapper.site_info.address);
  727. return false;
  728. };
  729. })(this));
  730. this.tag.find("#button-delete").off("click touchend").on("click touchend", (function(_this) {
  731. return function() {
  732. _this.wrapper.displayConfirm("Are you sure?", ["Delete this site", "Blacklist"], function(confirmed) {
  733. if (confirmed === 1) {
  734. _this.tag.find("#button-delete").addClass("loading");
  735. return _this.wrapper.ws.cmd("siteDelete", _this.wrapper.site_info.address, function() {
  736. return document.location = $(".fixbutton-bg").attr("href");
  737. });
  738. } else if (confirmed === 2) {
  739. return _this.wrapper.displayPrompt("Blacklist this site", "text", "Delete and Blacklist", "Reason", function(reason) {
  740. _this.tag.find("#button-delete").addClass("loading");
  741. _this.wrapper.ws.cmd("siteblockAdd", [_this.wrapper.site_info.address, reason]);
  742. return _this.wrapper.ws.cmd("siteDelete", _this.wrapper.site_info.address, function() {
  743. return document.location = $(".fixbutton-bg").attr("href");
  744. });
  745. });
  746. }
  747. });
  748. return false;
  749. };
  750. })(this));
  751. this.tag.find("#checkbox-owned").off("click touchend").on("click touchend", (function(_this) {
  752. return function() {
  753. return _this.wrapper.ws.cmd("siteSetOwned", [_this.tag.find("#checkbox-owned").is(":checked")]);
  754. };
  755. })(this));
  756. this.tag.find("#checkbox-autodownloadoptional").off("click touchend").on("click touchend", (function(_this) {
  757. return function() {
  758. return _this.wrapper.ws.cmd("siteSetAutodownloadoptional", [_this.tag.find("#checkbox-autodownloadoptional").is(":checked")]);
  759. };
  760. })(this));
  761. this.tag.find("#button-identity").off("click touchend").on("click touchend", (function(_this) {
  762. return function() {
  763. _this.wrapper.ws.cmd("certSelect");
  764. return false;
  765. };
  766. })(this));
  767. this.tag.find("#button-settings").off("click touchend").on("click touchend", (function(_this) {
  768. return function() {
  769. _this.wrapper.ws.cmd("fileGet", "content.json", function(res) {
  770. var data, json_raw;
  771. data = JSON.parse(res);
  772. data["title"] = $("#settings-title").val();
  773. data["description"] = $("#settings-description").val();
  774. json_raw = unescape(encodeURIComponent(JSON.stringify(data, void 0, '\t')));
  775. return _this.wrapper.ws.cmd("fileWrite", ["content.json", btoa(json_raw), true], function(res) {
  776. if (res !== "ok") {
  777. return _this.wrapper.notifications.add("file-write", "error", "File write error: " + res);
  778. } else {
  779. _this.wrapper.notifications.add("file-write", "done", "Site settings saved!", 5000);
  780. if (_this.wrapper.site_info.privatekey) {
  781. _this.wrapper.ws.cmd("siteSign", {
  782. privatekey: "stored",
  783. inner_path: "content.json",
  784. update_changed_files: true
  785. });
  786. }
  787. return _this.updateHtmlTag();
  788. }
  789. });
  790. });
  791. return false;
  792. };
  793. })(this));
  794. this.tag.find("#link-directory").off("click touchend").on("click touchend", (function(_this) {
  795. return function() {
  796. _this.wrapper.ws.cmd("serverShowdirectory", ["site", _this.wrapper.site_info.address]);
  797. return false;
  798. };
  799. })(this));
  800. this.tag.find("#link-copypeers").off("click touchend").on("click touchend", (function(_this) {
  801. return function(e) {
  802. var copy_text, handler;
  803. copy_text = e.currentTarget.href;
  804. handler = function(e) {
  805. e.clipboardData.setData('text/plain', copy_text);
  806. e.preventDefault();
  807. _this.wrapper.notifications.add("copy", "done", "Site address with peers copied to your clipboard", 5000);
  808. return document.removeEventListener('copy', handler, true);
  809. };
  810. document.addEventListener('copy', handler, true);
  811. document.execCommand('copy');
  812. return false;
  813. };
  814. })(this));
  815. $(document).on("click touchend", (function(_this) {
  816. return function() {
  817. var ref, ref1;
  818. if ((ref = _this.tag) != null) {
  819. ref.find("#button-sign-publish-menu").removeClass("visible");
  820. }
  821. return (ref1 = _this.tag) != null ? ref1.find(".contents + .flex").removeClass("sign-publish-flex") : void 0;
  822. };
  823. })(this));
  824. this.tag.find(".contents-content").off("click touchend").on("click touchend", (function(_this) {
  825. return function(e) {
  826. $("#input-contents").val(e.currentTarget.innerText);
  827. return false;
  828. };
  829. })(this));
  830. menu = new Menu(this.tag.find("#menu-sign-publish"));
  831. menu.elem.css("margin-top", "-130px");
  832. menu.addItem("Sign", (function(_this) {
  833. return function() {
  834. var inner_path;
  835. inner_path = _this.tag.find("#input-contents").val();
  836. _this.wrapper.ws.cmd("fileRules", {
  837. inner_path: inner_path
  838. }, function(res) {
  839. var ref;
  840. if (_this.wrapper.site_info.privatekey) {
  841. return _this.wrapper.ws.cmd("siteSign", {
  842. privatekey: "stored",
  843. inner_path: inner_path,
  844. update_changed_files: true
  845. }, function(res) {
  846. if (res === "ok") {
  847. return _this.wrapper.notifications.add("sign", "done", inner_path + " Signed!", 5000);
  848. }
  849. });
  850. } else if (ref = _this.wrapper.site_info.auth_address, indexOf.call(res.signers, ref) >= 0) {
  851. return _this.wrapper.ws.cmd("siteSign", {
  852. privatekey: null,
  853. inner_path: inner_path,
  854. update_changed_files: true
  855. }, function(res) {
  856. if (res === "ok") {
  857. return _this.wrapper.notifications.add("sign", "done", inner_path + " Signed!", 5000);
  858. }
  859. });
  860. } else {
  861. return _this.wrapper.displayPrompt("Enter your private key:", "password", "Sign", "", function(privatekey) {
  862. return _this.wrapper.ws.cmd("siteSign", {
  863. privatekey: privatekey,
  864. inner_path: inner_path,
  865. update_changed_files: true
  866. }, function(res) {
  867. if (res === "ok") {
  868. return _this.wrapper.notifications.add("sign", "done", inner_path + " Signed!", 5000);
  869. }
  870. });
  871. });
  872. }
  873. });
  874. _this.tag.find(".contents + .flex").removeClass("active");
  875. return menu.hide();
  876. };
  877. })(this));
  878. menu.addItem("Publish", (function(_this) {
  879. return function() {
  880. var inner_path;
  881. inner_path = _this.tag.find("#input-contents").val();
  882. _this.wrapper.ws.cmd("sitePublish", {
  883. "inner_path": inner_path,
  884. "sign": false
  885. });
  886. _this.tag.find(".contents + .flex").removeClass("active");
  887. return menu.hide();
  888. };
  889. })(this));
  890. this.tag.find("#menu-sign-publish").off("click touchend").on("click touchend", (function(_this) {
  891. return function() {
  892. if (window.visible_menu === menu) {
  893. _this.tag.find(".contents + .flex").removeClass("active");
  894. menu.hide();
  895. } else {
  896. _this.tag.find(".contents + .flex").addClass("active");
  897. _this.tag.find(".content-wrapper").prop("scrollTop", 10000);
  898. menu.show();
  899. }
  900. return false;
  901. };
  902. })(this));
  903. $("body").on("click", (function(_this) {
  904. return function() {
  905. if (_this.tag) {
  906. return _this.tag.find(".contents + .flex").removeClass("active");
  907. }
  908. };
  909. })(this));
  910. this.tag.find("#button-sign-publish").off("click touchend").on("click touchend", (function(_this) {
  911. return function() {
  912. var inner_path;
  913. inner_path = _this.tag.find("#input-contents").val();
  914. _this.wrapper.ws.cmd("fileRules", {
  915. inner_path: inner_path
  916. }, function(res) {
  917. var ref;
  918. if (_this.wrapper.site_info.privatekey) {
  919. return _this.wrapper.ws.cmd("sitePublish", {
  920. privatekey: "stored",
  921. inner_path: inner_path,
  922. sign: true,
  923. update_changed_files: true
  924. }, function(res) {
  925. if (res === "ok") {
  926. return _this.wrapper.notifications.add("sign", "done", inner_path + " Signed and published!", 5000);
  927. }
  928. });
  929. } else if (ref = _this.wrapper.site_info.auth_address, indexOf.call(res.signers, ref) >= 0) {
  930. return _this.wrapper.ws.cmd("sitePublish", {
  931. privatekey: null,
  932. inner_path: inner_path,
  933. sign: true,
  934. update_changed_files: true
  935. }, function(res) {
  936. if (res === "ok") {
  937. return _this.wrapper.notifications.add("sign", "done", inner_path + " Signed and published!", 5000);
  938. }
  939. });
  940. } else {
  941. return _this.wrapper.displayPrompt("Enter your private key:", "password", "Sign", "", function(privatekey) {
  942. return _this.wrapper.ws.cmd("sitePublish", {
  943. privatekey: privatekey,
  944. inner_path: inner_path,
  945. sign: true,
  946. update_changed_files: true
  947. }, function(res) {
  948. if (res === "ok") {
  949. return _this.wrapper.notifications.add("sign", "done", inner_path + " Signed and published!", 5000);
  950. }
  951. });
  952. });
  953. }
  954. });
  955. return false;
  956. };
  957. })(this));
  958. this.tag.find(".close").off("click touchend").on("click touchend", (function(_this) {
  959. return function(e) {
  960. _this.close();
  961. return false;
  962. };
  963. })(this));
  964. return this.loadGlobe();
  965. };
  966. Sidebar.prototype.close = function() {
  967. this.move_lock = "x";
  968. this.startDrag();
  969. return this.stopDrag();
  970. };
  971. Sidebar.prototype.onClosed = function() {
  972. $(window).off("resize");
  973. $(window).on("resize", this.resized);
  974. $(document.body).css("transition", "0.6s ease-in-out").removeClass("body-sidebar").on(transitionEnd, (function(_this) {
  975. return function(e) {
  976. if (e.target === document.body && !$(document.body).hasClass("body-sidebar") && !$(document.body).hasClass("body-internals")) {
  977. $(document.body).css("height", "auto").css("perspective", "").css("will-change", "").css("transition", "").off(transitionEnd);
  978. return _this.unloadGlobe();
  979. }
  980. };
  981. })(this));
  982. return this.wrapper.setSiteInfo = this.original_set_site_info;
  983. };
  984. Sidebar.prototype.loadGlobe = function() {
  985. console.log("loadGlobe", this.tag.find(".globe")[0], this.tag.find(".globe").hasClass("loading"));
  986. if (this.tag.find(".globe").hasClass("loading")) {
  987. return setTimeout(((function(_this) {
  988. return function() {
  989. var script_tag;
  990. if (typeof DAT === "undefined") {
  991. script_tag = $("<script>");
  992. script_tag.attr("nonce", _this.wrapper.script_nonce);
  993. script_tag.attr("src", "/uimedia/globe/all.js");
  994. script_tag.on("load", _this.displayGlobe);
  995. return document.head.appendChild(script_tag[0]);
  996. } else {
  997. return _this.displayGlobe();
  998. }
  999. };
  1000. })(this)), 600);
  1001. }
  1002. };
  1003. Sidebar.prototype.displayGlobe = function() {
  1004. var img;
  1005. img = new Image();
  1006. img.src = "/uimedia/globe/world.jpg";
  1007. return img.onload = (function(_this) {
  1008. return function() {
  1009. return _this.wrapper.ws.cmd("sidebarGetPeers", [], function(globe_data) {
  1010. var e, ref, ref1, ref2;
  1011. if (_this.globe) {
  1012. _this.globe.scene.remove(_this.globe.points);
  1013. _this.globe.addData(globe_data, {
  1014. format: 'magnitude',
  1015. name: "hello",
  1016. animated: false
  1017. });
  1018. _this.globe.createPoints();
  1019. return (ref = _this.tag) != null ? ref.find(".globe").removeClass("loading") : void 0;
  1020. } else if (typeof DAT !== "undefined") {
  1021. try {
  1022. _this.globe = new DAT.Globe(_this.tag.find(".globe")[0], {
  1023. "imgDir": "/uimedia/globe/"
  1024. });
  1025. _this.globe.addData(globe_data, {
  1026. format: 'magnitude',
  1027. name: "hello"
  1028. });
  1029. _this.globe.createPoints();
  1030. _this.globe.animate();
  1031. } catch (error) {
  1032. e = error;
  1033. console.log("WebGL error", e);
  1034. if ((ref1 = _this.tag) != null) {
  1035. ref1.find(".globe").addClass("error").text("WebGL not supported");
  1036. }
  1037. }
  1038. return (ref2 = _this.tag) != null ? ref2.find(".globe").removeClass("loading") : void 0;
  1039. }
  1040. });
  1041. };
  1042. })(this);
  1043. };
  1044. Sidebar.prototype.unloadGlobe = function() {
  1045. if (!this.globe) {
  1046. return false;
  1047. }
  1048. this.globe.unload();
  1049. return this.globe = null;
  1050. };
  1051. return Sidebar;
  1052. })(Class);
  1053. wrapper = window.wrapper;
  1054. setTimeout((function() {
  1055. return window.sidebar = new Sidebar(wrapper);
  1056. }), 500);
  1057. window.transitionEnd = 'transitionend webkitTransitionEnd oTransitionEnd otransitionend';
  1058. }).call(this);
  1059. /* ---- plugins/Sidebar/media/morphdom.js ---- */
  1060. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.morphdom = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  1061. var specialElHandlers = {
  1062. /**
  1063. * Needed for IE. Apparently IE doesn't think
  1064. * that "selected" is an attribute when reading
  1065. * over the attributes using selectEl.attributes
  1066. */
  1067. OPTION: function(fromEl, toEl) {
  1068. if ((fromEl.selected = toEl.selected)) {
  1069. fromEl.setAttribute('selected', '');
  1070. } else {
  1071. fromEl.removeAttribute('selected', '');
  1072. }
  1073. },
  1074. /**
  1075. * The "value" attribute is special for the <input> element
  1076. * since it sets the initial value. Changing the "value"
  1077. * attribute without changing the "value" property will have
  1078. * no effect since it is only used to the set the initial value.
  1079. * Similar for the "checked" attribute.
  1080. */
  1081. /*INPUT: function(fromEl, toEl) {
  1082. fromEl.checked = toEl.checked;
  1083. fromEl.value = toEl.value;
  1084. if (!toEl.hasAttribute('checked')) {
  1085. fromEl.removeAttribute('checked');
  1086. }
  1087. if (!toEl.hasAttribute('value')) {
  1088. fromEl.removeAttribute('value');
  1089. }
  1090. }*/
  1091. };
  1092. function noop() {}
  1093. /**
  1094. * Loop over all of the attributes on the target node and make sure the
  1095. * original DOM node has the same attributes. If an attribute
  1096. * found on the original node is not on the new node then remove it from
  1097. * the original node
  1098. * @param {HTMLElement} fromNode
  1099. * @param {HTMLElement} toNode
  1100. */
  1101. function morphAttrs(fromNode, toNode) {
  1102. var attrs = toNode.attributes;
  1103. var i;
  1104. var attr;
  1105. var attrName;
  1106. var attrValue;
  1107. var foundAttrs = {};
  1108. for (i=attrs.length-1; i>=0; i--) {
  1109. attr = attrs[i];
  1110. if (attr.specified !== false) {
  1111. attrName = attr.name;
  1112. attrValue = attr.value;
  1113. foundAttrs[attrName] = true;
  1114. if (fromNode.getAttribute(attrName) !== attrValue) {
  1115. fromNode.setAttribute(attrName, attrValue);
  1116. }
  1117. }
  1118. }
  1119. // Delete any extra attributes found on the original DOM element that weren't
  1120. // found on the target element.
  1121. attrs = fromNode.attributes;
  1122. for (i=attrs.length-1; i>=0; i--) {
  1123. attr = attrs[i];
  1124. if (attr.specified !== false) {
  1125. attrName = attr.name;
  1126. if (!foundAttrs.hasOwnProperty(attrName)) {
  1127. fromNode.removeAttribute(attrName);
  1128. }
  1129. }
  1130. }
  1131. }
  1132. /**
  1133. * Copies the children of one DOM element to another DOM element
  1134. */
  1135. function moveChildren(from, to) {
  1136. var curChild = from.firstChild;
  1137. while(curChild) {
  1138. var nextChild = curChild.nextSibling;
  1139. to.appendChild(curChild);
  1140. curChild = nextChild;
  1141. }
  1142. return to;
  1143. }
  1144. function morphdom(fromNode, toNode, options) {
  1145. if (!options) {
  1146. options = {};
  1147. }
  1148. if (typeof toNode === 'string') {
  1149. var newBodyEl = document.createElement('body');
  1150. newBodyEl.innerHTML = toNode;
  1151. toNode = newBodyEl.childNodes[0];
  1152. }
  1153. var savedEls = {}; // Used to save off DOM elements with IDs
  1154. var unmatchedEls = {};
  1155. var onNodeDiscarded = options.onNodeDiscarded || noop;
  1156. var onBeforeMorphEl = options.onBeforeMorphEl || noop;
  1157. var onBeforeMorphElChildren = options.onBeforeMorphElChildren || noop;
  1158. function removeNodeHelper(node, nestedInSavedEl) {
  1159. var id = node.id;
  1160. // If the node has an ID then save it off since we will want
  1161. // to reuse it in case the target DOM tree has a DOM element
  1162. // with the same ID
  1163. if (id) {
  1164. savedEls[id] = node;
  1165. } else if (!nestedInSavedEl) {
  1166. // If we are not nested in a saved element then we know that this node has been
  1167. // completely discarded and will not exist in the final DOM.
  1168. onNodeDiscarded(node);
  1169. }
  1170. if (node.nodeType === 1) {
  1171. var curChild = node.firstChild;
  1172. while(curChild) {
  1173. removeNodeHelper(curChild, nestedInSavedEl || id);
  1174. curChild = curChild.nextSibling;
  1175. }
  1176. }
  1177. }
  1178. function walkDiscardedChildNodes(node) {
  1179. if (node.nodeType === 1) {
  1180. var curChild = node.firstChild;
  1181. while(curChild) {
  1182. if (!curChild.id) {
  1183. // We only want to handle nodes that don't have an ID to avoid double
  1184. // walking the same saved element.
  1185. onNodeDiscarded(curChild);
  1186. // Walk recursively
  1187. walkDiscardedChildNodes(curChild);
  1188. }
  1189. curChild = curChild.nextSibling;
  1190. }
  1191. }
  1192. }
  1193. function removeNode(node, parentNode, alreadyVisited) {
  1194. parentNode.removeChild(node);
  1195. if (alreadyVisited) {
  1196. if (!node.id) {
  1197. onNodeDiscarded(node);
  1198. walkDiscardedChildNodes(node);
  1199. }
  1200. } else {
  1201. removeNodeHelper(node);
  1202. }
  1203. }
  1204. function morphEl(fromNode, toNode, alreadyVisited) {
  1205. if (toNode.id) {
  1206. // If an element with an ID is being morphed then it is will be in the final
  1207. // DOM so clear it out of the saved elements collection
  1208. delete savedEls[toNode.id];
  1209. }
  1210. if (onBeforeMorphEl(fromNode, toNode) === false) {
  1211. return;
  1212. }
  1213. morphAttrs(fromNode, toNode);
  1214. if (onBeforeMorphElChildren(fromNode, toNode) === false) {
  1215. return;
  1216. }
  1217. var curToNodeChild = toNode.firstChild;
  1218. var curFromNodeChild = fromNode.firstChild;
  1219. var curToNodeId;
  1220. var fromNextSibling;
  1221. var toNextSibling;
  1222. var savedEl;
  1223. var unmatchedEl;
  1224. outer: while(curToNodeChild) {
  1225. toNextSibling = curToNodeChild.nextSibling;
  1226. curToNodeId = curToNodeChild.id;
  1227. while(curFromNodeChild) {
  1228. var curFromNodeId = curFromNodeChild.id;
  1229. fromNextSibling = curFromNodeChild.nextSibling;
  1230. if (!alreadyVisited) {
  1231. if (curFromNodeId && (unmatchedEl = unmatchedEls[curFromNodeId])) {
  1232. unmatchedEl.parentNode.replaceChild(curFromNodeChild, unmatchedEl);
  1233. morphEl(curFromNodeChild, unmatchedEl, alreadyVisited);
  1234. curFromNodeChild = fromNextSibling;
  1235. continue;
  1236. }
  1237. }
  1238. var curFromNodeType = curFromNodeChild.nodeType;
  1239. if (curFromNodeType === curToNodeChild.nodeType) {
  1240. var isCompatible = false;
  1241. if (curFromNodeType === 1) { // Both nodes being compared are Element nodes
  1242. if (curFromNodeChild.tagName === curToNodeChild.tagName) {
  1243. // We have compatible DOM elements
  1244. if (curFromNodeId || curToNodeId) {
  1245. // If either DOM element has an ID then we handle
  1246. // those differently since we want to match up
  1247. // by ID
  1248. if (curToNodeId === curFromNodeId) {
  1249. isCompatible = true;
  1250. }
  1251. } else {
  1252. isCompatible = true;
  1253. }
  1254. }
  1255. if (isCompatible) {
  1256. // We found compatible DOM elements so add a
  1257. // task to morph the compatible DOM elements
  1258. morphEl(curFromNodeChild, curToNodeChild, alreadyVisited);
  1259. }
  1260. } else if (curFromNodeType === 3) { // Both nodes being compared are Text nodes
  1261. isCompatible = true;
  1262. curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
  1263. }
  1264. if (isCompatible) {
  1265. curToNodeChild = toNextSibling;
  1266. curFromNodeChild = fromNextSibling;
  1267. continue outer;
  1268. }
  1269. }
  1270. // No compatible match so remove the old node from the DOM
  1271. removeNode(curFromNodeChild, fromNode, alreadyVisited);
  1272. curFromNodeChild = fromNextSibling;
  1273. }
  1274. if (curToNodeId) {
  1275. if ((savedEl = savedEls[curToNodeId])) {
  1276. morphEl(savedEl, curToNodeChild, true);
  1277. curToNodeChild = savedEl; // We want to append the saved element instead
  1278. } else {
  1279. // The current DOM element in the target tree has an ID
  1280. // but we did not find a match in any of the corresponding
  1281. // siblings. We just put the target element in the old DOM tree
  1282. // but if we later find an element in the old DOM tree that has
  1283. // a matching ID then we will replace the target element
  1284. // with the corresponding old element and morph the old element
  1285. unmatchedEls[curToNodeId] = curToNodeChild;
  1286. }
  1287. }
  1288. // If we got this far then we did not find a candidate match for our "to node"
  1289. // and we exhausted all of the children "from" nodes. Therefore, we will just
  1290. // append the current "to node" to the end
  1291. fromNode.appendChild(curToNodeChild);
  1292. curToNodeChild = toNextSibling;
  1293. curFromNodeChild = fromNextSibling;
  1294. }
  1295. // We have processed all of the "to nodes". If curFromNodeChild is non-null then
  1296. // we still have some from nodes left over that need to be removed
  1297. while(curFromNodeChild) {
  1298. fromNextSibling = curFromNodeChild.nextSibling;
  1299. removeNode(curFromNodeChild, fromNode, alreadyVisited);
  1300. curFromNodeChild = fromNextSibling;
  1301. }
  1302. var specialElHandler = specialElHandlers[fromNode.tagName];
  1303. if (specialElHandler) {
  1304. specialElHandler(fromNode, toNode);
  1305. }
  1306. }
  1307. var morphedNode = fromNode;
  1308. var morphedNodeType = morphedNode.nodeType;
  1309. var toNodeType = toNode.nodeType;
  1310. // Handle the case where we are given two DOM nodes that are not
  1311. // compatible (e.g. <div> --> <span> or <div> --> TEXT)
  1312. if (morphedNodeType === 1) {
  1313. if (toNodeType === 1) {
  1314. if (morphedNode.tagName !== toNode.tagName) {
  1315. onNodeDiscarded(fromNode);
  1316. morphedNode = moveChildren(morphedNode, document.createElement(toNode.tagName));
  1317. }
  1318. } else {
  1319. // Going from an element node to a text node
  1320. return toNode;
  1321. }
  1322. } else if (morphedNodeType === 3) { // Text node
  1323. if (toNodeType === 3) {
  1324. morphedNode.nodeValue = toNode.nodeValue;
  1325. return morphedNode;
  1326. } else {
  1327. onNodeDiscarded(fromNode);
  1328. // Text node to something else
  1329. return toNode;
  1330. }
  1331. }
  1332. morphEl(morphedNode, toNode, false);
  1333. // Fire the "onNodeDiscarded" event for any saved elements
  1334. // that never found a new home in the morphed DOM
  1335. for (var savedElId in savedEls) {
  1336. if (savedEls.hasOwnProperty(savedElId)) {
  1337. var savedEl = savedEls[savedElId];
  1338. onNodeDiscarded(savedEl);
  1339. walkDiscardedChildNodes(savedEl);
  1340. }
  1341. }
  1342. if (morphedNode !== fromNode && fromNode.parentNode) {
  1343. fromNode.parentNode.replaceChild(morphedNode, fromNode);
  1344. }
  1345. return morphedNode;
  1346. }
  1347. module.exports = morphdom;
  1348. },{}]},{},[1])(1)
  1349. });