1 2let init_modules = {}; 3 4function isDict(obj) { 5 return (obj.constructor == Object) 6} 7 8/* 9 * Returns an array of keys which are in base_obj but not in compare_obj 10 */ 11function objKeyDiff(base_obj, compare_obj) { 12 const res = [] 13 14 Object.keys(base_obj).forEach(key => { 15 if (!(key in compare_obj)) { 16 res.push(key) 17 } 18 }); 19 return res; 20} 21 22class ViewHistoryManager { 23 constructor(magazine_id) { 24 this.magazine_id = magazine_id; 25 this.view_history = this._read_history(); 26 const vhm = this; 27 28 $(window).bind('storage', function (e) { 29 if (e.originalEvent.key == vhm._storage_key) 30 { 31 const old_history = vhm._parse_history(e.originalEvent.oldValue); 32 const new_history = vhm._parse_history(e.originalEvent.newValue); 33 vhm.view_history = new_history; 34 const removed_entries = objKeyDiff(old_history, new_history); 35 const added_entries = objKeyDiff(new_history, old_history); 36 $(window).trigger( "history_changed", [vhm. magazine_id, added_entries, removed_entries ] ); 37 } 38 }); 39 } 40 41 get _storage_key() { 42 return "view_history_" + this.magazine_id; 43 } 44 45 _parse_history(history) { 46 let res; 47 48 try { 49 res = JSON.parse(history); 50 if (!isDict(res)) { 51 throw "Error: Expecting dictionary!"; 52 } 53 } 54 catch (err) { 55 res = {}; // Using dictionary instead of array for O(1) search complexity 56 } 57 58 return res; 59 } 60 61 _write_history() { 62 window.localStorage.setItem(this._storage_key, JSON.stringify(this.view_history)); 63 } 64 65 _read_history() { 66 return this._parse_history(window.localStorage.getItem(this._storage_key)) 67 } 68 69 is_viewed(issue_id) { 70 return issue_id in this.view_history; 71 } 72 73 mark_as_viewed(issue_id) { 74 if (!this.is_viewed(issue_id)) { 75 this.view_history[issue_id] = 1; 76 this._write_history(); 77 } 78 } 79 80 mark_as_not_viewed(issue_id) { 81 if (this.is_viewed(issue_id)) { 82 delete this.view_history[issue_id]; 83 this._write_history(); 84 } 85 } 86 87 get viewed_issues() { 88 return [...Object.keys(this.view_history)]; 89 } 90} 91 92init_modules['magazines_init'] = function() { 93 $("#magazines_container img.lazyload").lazyload(); 94 95 const vhm = {}; 96 97 for (const magazine_id of magazine_ids.split(",")) { 98 vhm[magazine_id] = new ViewHistoryManager(magazine_id); 99 } 100 101 $("#magazines_container .magazine-card").mousedown(function(e) { 102 // We don't simply use "click" since it doesn't catch middle-click 103 switch(e.which) 104 { 105 case 1: // Left Click 106 case 2: // Middle Click 107 const obj = $(this); 108 const [magazine_id, issue_id] = obj.attr('id').split("_"); 109 if (!vhm[magazine_id].is_viewed(issue_id)) 110 { 111 vhm[magazine_id].mark_as_viewed(issue_id); 112 setTimeout(function(){ 113 obj.addClass("is_read"); 114 }, 500); 115 116 } 117 break; 118 } 119 }); 120 121 $("#magazines_container .view_indicator").click(function(e){ 122 e.preventDefault(); 123 }); 124 125 $(window).on( "history_changed", function( event, magazine_id, read_issues, unread_issues ) { 126 for (const issue_id of read_issues) { 127 $("#" + magazine_id + "_" + issue_id).addClass("is_read"); 128 } 129 for (const issue_id of unread_issues) { 130 $("#" + magazine_id + "_" + issue_id).removeClass("is_read"); 131 } 132 }); 133 134 for (const magazine_id in vhm) { 135 $(window).trigger( "history_changed", [ magazine_id, vhm[magazine_id].viewed_issues, [] ] ); 136 } 137 138 const mark_as_unread = function(magazine_id, issue_id) { 139 vhm[magazine_id].mark_as_not_viewed(issue_id); 140 setTimeout(function(){ 141 $("#" + magazine_id + "_" + issue_id).removeClass("is_read"); 142 }, 100); 143 } 144 145 $('#magazines_container .view_indicator').popover({ 146 title: '××××× ×× × ×§×¨× ××¢×ר', 147 placement: 'top', 148 //content: '<div class="text-center"><a href="javascript:void(0);" onclick="javascript:mark_as_unread">[×סר ס××××]</a></div>', 149 content: function() { 150 const [magazine_id, issue_id] = $(this).parents(".magazine-card:first").attr('id').split("_"); 151 const a = $("<a href='javascript:void(0);'>[×סר ס××××]</a>").click(function(){mark_as_unread(magazine_id, issue_id)}); 152 const div = $("<div class='text-center'></div>").append(a); 153 return div; 154 }, 155 156 html: true, 157 trigger: "manual", 158 html: true, 159 animation: true 160 }) 161 .on("mouseenter", function() { 162 var _this = this; 163 $(this).popover("show"); 164 $(".popover").on("mouseleave", function() { 165 $(_this).popover('hide'); 166 }); 167 }).on("mouseleave", function() { 168 var _this = this; 169 setTimeout(function() { 170 if (!$(".popover:hover").length) { 171 $(_this).popover("hide"); 172 } 173 }, 300); 174 }); 175}; 176 177if (typeof(js_module_init) != "undefined") { 178 init_modules[js_module_init](); 179}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.