1Vue.component('basket', { 2 props: [ 3 'basket', 4 'source', 5 'mobile', 6 'showAbstracts', 7 'getLinkText', 8 'getDigitalLibraryLink' 9 ], 10 data: function () { 11 return { 12 editNotes: true 13 } 14 }, 15 mounted: function () { 16 }, 17 computed: { 18 btnStyle: function () { 19 return { 20 backgroundColor: "black", 21 color: "white", 22 height: "28px", 23 fontSize: "10px", 24 border: "none", 25 padding: "4px", 26 cursor: "pointer" 27 } 28 } 29 }, 30 methods: { 31 getSectionOptions: function (articleItem, currentSection) { 32 var self = this; 33 var options = []; 34 35 if (this.basket.sections.length === 0) { 36 this.basket.sections.push({ 37 name: "Default", 38 intro: "", 39 items: [] 40 }); 41 } 42 43 this.basket.sections.filter(function (s) { 44 return s !== currentSection; 45 }
45).forEach(function (section) { 46 if (!section.items) { 47 section.item = []; 48 } 49 options.push({ 50 title: section.name, 51 action: function () { 52 currentSection.items.splice(currentSection.items.indexOf(articleItem), 1); 53 section.items.push(articleItem); 54 self.saveBasketData(); 55 } 56 }); 57 }); 58 59 options.push({ 60 title: "<new section>", 61 action: function () { 62 var tag = prompt("Please enter the section name:", ""); 63 64 if (tag != null) { 65 var section = { 66 name: tag, 67 intro: "", 68 items: [] 69 }; 70 self.basket.sections.push(section); 71 section.items.push(articleItem); 72 self.saveBasketData(); 73 } 74 } 75 }); 76 77 return options; 78 }, 79 sortSections: function () { 80 this.basket.sections = this.basket.sections.sort(function (a, b) { 81 return a.name.localeCompare(b.name); 82 }); 83 84 this.basket.sections.forEach(function (section) { 85 section.items = section.items.sort(function (a, b) { 86 return a.article.number - b.article.number; 87 }); 88 }); 89 }, 90 sortSectionsDesc: function () { 91 this.basket.sections = this.basket.sections.sort(function (a, b) { 92 return b.name.localeCompare(a.name); 93 }); 94 95 this.basket.sections.forEach(function (section) { 96 section.items = section.items.sort(function (a, b) { 97 return b.article.number - a.article.number; 98 }); 99 }); 100 }, 101 exportBasket: function () { 102 var text = JSON.stringify(this.basket, null, 2); 103 var filename = "collection.json"; 104 this.download(text, filename, "text/plain;charset=utf-8"); 105 }, 106 importBasket: function (e) { 107 var self = this; 108 var files = e.target.files || e.dataTransfer.files; 109 if (!files.length) return; 110 var reader = new FileReader(); 111 reader.onload = function (e) { 112 if (reader.result) { 113 self.clearAll(); 114 self.updateBasket(JSON.parse(reader.result)); 115 self.saveBasketData(); 116 } 117 } 118 reader.readAsText(files[0]); 119 }, 120 download: function (data, filename, type) { 121 var file = new Blob([data], {type: type}); 122 if (window.navigator.msSaveOrOpenBlob) // IE10+ 123 window.navigator.msSaveOrOpenBlob(file, filename); 124 else { // Others 125 var a = document.createElement("a"), url = URL.createObjectURL(file); 126 a.href = url; 127 a.download = filename; 128 document.body.appendChild(a); 129 a.click(); 130 setTimeout(function () { 131 document.body.removeChild(a); 132 window.URL.revokeObjectURL(url); 133 }, 0); 134 } 135 }, 136 clearAll: function () { 137 this.basket.id = ""; 138 this.basket.intro = ""; 139 this.basket.type = "359° Review"; 140 this.basket.title = "New Collection"; 141 this.basket.subtitle = "editor Željko ObrenoviÄ"; 142 this.basket.imageUri = "images/logo.png"; 143 this.basket.logoUri = "images/logo.png"; 144 this.basket.iconUri = ""; 145 this.basket.sections = []; 146 147 this.saveBasketData(); 148 }, 149 saveBasketData: function () { 150 bus.$emit("saveBasketData", {}); 151 }, 152 moveSectionUp: function (section) { 153 var index = this.basket.sections.indexOf(section); 154 155 if (index > 0) { 156 this.array_move(this.basket.sections, index, index - 1); 157 } 158 159 this.saveBasketData(); 160 }, 161 moveSectionDown: function (section) { 162 var index = this.basket.sections.indexOf(section); 163 164 if (index < this.basket.sections.length - 1) { 165 this.array_move(this.basket.sections, index, index + 1); 166 this.saveBasketData(); 167 } 168 169 this.saveBasketData(); 170 }, 171 moveArticleUp: function (section, articleItem) { 172 var index = section.items.indexOf(articleItem); 173 174 if (index > 0) { 175 this.array_move(section.items, index, index - 1); 176 this.saveBasketData(); 177 } 178 179 this.saveBasketData(); 180 }, 181 moveArticleDown: function (section, articleItem) { 182 var index = section.items.indexOf(articleItem); 183 184 if (index < section.items.length - 1) { 185 this.array_move(section.items, index, index + 1); 186 this.saveBasketData(); 187 } 188 189 this.saveBasketData(); 190 }, 191 moveArticleFirst: function (section, articleItem) { 192 var index = section.items.indexOf(articleItem); 193 194 if (index > 0) { 195 this.array_move(section.items, index, 0); 196 this.saveBasketData(); 197 } 198 199 this.saveBasketData(); 200 }, 201 moveArticleLast: function (section, articleItem) { 202 var index = section.items.indexOf(articleItem); 203 204 if (index < section.items.length - 1) { 205 this.array_move(section.items, index, section.items.length - 1); 206 this.saveBasketData(); 207 } 208 209 this.saveBasketData(); 210 }, 211 array_move: function (arr, old_index, new_index) { 212 if (new_index >= arr.length) { 213 var k = new_index - arr.length + 1; 214 while (k--) { 215 arr.push(undefined); 216 } 217 } 218 arr.splice(new_index, 0, arr.splice(old_index, 1)[0]); 219 return arr; // for testing 220 }, 221 editIntro: function () { 222 var self = this; 223 bus.$emit('showModalEditor', { 224 object: self.basket, 225 property: "intro", 226 title: "Intro", 227 lang: "markdown", 228 onSave: function () { 229 self.saveBasketData(); 230 } 231 }); 232 }, 233 editAbstract: function (article) { 234 var self = this; 235 bus.$emit('showModalEditor', { 236 object: article, 237 property: "quoteAbstract", 238 title: "Abstract", 239 lang: "markdown", 240 onSave: function () { 241 self.saveBasketData(); 242 } 243 }); 244 }, 245 editOneLiner: function (articleItem) { 246 var self = this; 247 bus.$emit('showModalEditor', { 248 object: articleItem, 249 property: "oneLiner", 250 title: "One-liner:", 251 lang: "markdown", 252 onSave: function () { 253 self.saveBasketData(); 254 } 255 }); 256 }, 257 editTitle: function () { 258 var self = this; 259 bus.$emit('showModalInput', { 260 object: self.basket, 261 property: "title", 262 title: "Title:", 263 lang: "text", 264 onSave: function () { 265 self.saveBasketData(); 266 } 267 }); 268 }, 269 editLogo: function () { 270 var self = this; 271 bus.$emit('showModalInput', { 272 object: self.basket, 273 property: "logoUri", 274 title: "Logo URL:", 275 lang: "text", 276 onSave: function () { 277 self.saveBasketData(); 278 } 279 }); 280 }, 281 editSectionField: function (section, fieldName) { 282 var self = this; 283 var oldTitle = section.title; 284 bus.$emit('showModalInput', { 285 object: section, 286 property: fieldName, 287 title: fieldName.toUpperCase() + ":", 288 lang: "text", 289 onSave: function () { 290 self.saveBasketData(); 291 } 292 }); 293 }, 294 editSectionIntro: function (section) { 295 var self = this; 296 bus.$emit('showModalEditor', { 297 object: section, 298 property: "intro", 299 title: "Intro:", 300 lang: "markdown", 301 onSave: function () { 302 self.saveBasketData(); 303 } 304 }); 305 }, 306 deleteSection: function (section) { 307 this.basket.sections.splice(this.basket.sections.indexOf(section), 1); 308 }, 309 sortNewestFirst: function (section) { 310 section.items = section.items.sort(function (a, b) { 311 var indexA = a.article.number; 312 var indexB = b.article.number; 313 return (indexA + b.article.year * 100) - (indexB + a.article.year * 100); 314 }); 315 316 this.saveBasketData(); 317 }, 318 sortOldestFirst: function (section) { 319 section.items = section.items.sort(function (a, b) { 320 var indexA = a.article.number; 321 var indexB = b.article.number; 322 return (indexB + a.article.year * 100) - (indexA + b.article.year * 100); 323 }); 324 325 this.saveBasketData(); 326 }, 327 updateBasket: function (data) { 328 if (data.id) this.basket.id = data.id; 329 if (data.type) this.basket.id = data.type; 330 if (data.title) this.basket.title = data.title;
331 if (data.subtitle) this.basket.subtitle = data.subtitle; 332 if (data.logoUri) this.basket.logoUri = data.logoUri; 333 if (data.imageUri) this.basket.imageUri = data.imageUri; 334 if (data.sections) this.basket.sections = data.sections; 335 if (data.intro) this.basket.intro = data.intro; 336 }, 337 editJson: function () { 338 var self = this; 339 var jsonData = { 340 data: JSON.stringify(self.basket, null, 2) 341 }; 342 bus.$emit('showModalEditor', { 343 object: jsonData, 344 property: "data", 345 title: "JSON:", 346 lang: "json", 347 onSave: function (json) { 348 self.updateBasket(JSON.parse(json)); 349 } 350 }); 351 }, 352 remove: function (section, articleItem) { 353 section.items.splice(section.items.indexOf(articleItem), 1); 354 this.saveBasketData(); 355 } 356 }, 357 template: "<div style='width=100%; max-width: 1200px;'>" 358 + " <label><input type='checkbox' v-model='editNotes'>edit</label>" 359 + " <button @click='saveBasketData()' class='dropbtn' :style='btnStyle'>SAVE</button>" 360 + " <button @click='clearAll()' class='dropbtn' :style='btnStyle'>CLEAR ALL</button>" 361 + " <label for='file-upload' class='dropbtn' :style='btnStyle'>Import...</label>" 362 + " <input id='file-upload' type='file' @change='importBasket' style='display: none;'/>" 363 + " <button @click='exportBasket()' class='dropbtn' :style='btnStyle'>EXPORT</button>" 364 + " <button @click.prevent='editLogo()' class='dropbtn' :style='btnStyle'>LOGO URL...</button>" 365 + " <button @click.prevent='editJson()' class='dropbtn' :style='btnStyle'>JSON...</button>" 366 + " <button @click.prevent='sortSections()' class='dropbtn' :style='btnStyle'>Sort Sections</button>" 367 + " <button @click.prevent='sortSectionsDesc()' class='dropbtn' :style='btnStyle'>Sort Sections (DESC)</button>" 368 + " <a target='_blank' href='collection.html'>Preview...</a>" 369 + " <span>" 370 + " <h1>{{ basket.title }}" 371 + " <button @click.prevent='editTitle()' class='dropbtn'>rename...</button>" 372 + " <button @click.prevent='editIntro()' class='dropbtn'>edit intro...</button></h1>" 373 + " <span v-html='mdToHtml(basket.intro)' style='font-size: 110%'></span>" 374 + " </span>" 375 + " <template v-for='section in basket.sections' style='group'>" 376 + " <h2 v-if='editNotes' style='margin-top: 60px'>{{ section.name }}" 377 + " <button @click='moveSectionUp(section)' class='dropbtn' :style='btnStyle'>up</button>" 378 + " <button @click='moveSectionDown(section)' class='dropbtn' :style='btnStyle'>down</button>" 379 + " | <button @click='editSectionField(section, \"name\")' class='dropbtn' :style='btnStyle'>rename...</button>" 380 + " <button @click='editSectionIntro(section)' class='dropbtn' :style='btnStyle'>edit intro...</button>" 381 + " | <button @click='sortNewestFirst(section)' class='dropbtn' :style='btnStyle'>newest first...</button>" 382 + " <button @click='sortOldestFirst(section)' class='dropbtn' :style='btnStyle'>oldest first...</button>" 383 + " | <button @click='deleteSection(section)' class='dropbtn' :style='btnStyle'>delete</button>" 384 + " </h2>" 385 + " <h2 v-if='!editNotes' style='margin-top: 60px'>{{ section.name }}</h2>" 386 + " <span v-html='mdToHtml(section.intro)' style='font-size: 110%'></span>" 387 + " <table style='width: 100%;'>" 388 + " <tr v-for='(articleItem, index) in section.items'>" 389 + " <td v-if='!mobile' style='text-align: right;' valign='top'>{{ index + 1 }}.</td>" 390 + " <td valign='top' style='border-bottom: 1px solid grey'>" 391 + " <article-abstract :article='articleItem.article'" 392 + " :show-authors='true'" 393 + " :source='source' :mobile='mobile'"
394 + " :show-all-abstracts='showAbstracts'" 395 + " :get-link-text='getLinkText'" 396 + " :one-liner='articleItem.oneLiner'" 397 + " :get-digital-library-link='getDigitalLibraryLink'>" 398 + " </article-abstract>" 399 + " </td>" 400 + " <td v-if='editNotes' style='width: 350px; border-bottom: 1px solid grey'>" 401 + " move: <button @click.stop='moveArticleUp(section, articleItem)' class='dropbtn' :style='btnStyle'>up</button>" 402 + " <button @click.stop='moveArticleDown(section, articleItem)' class='dropbtn' :style='btnStyle'>down</button> | " 403 + " <button @click.stop='moveArticleFirst(section, articleItem)' class='dropbtn' :style='btnStyle'>first</button>" 404 + " <button @click.stop='moveArticleLast(section, articleItem)' class='dropbtn' :style='btnStyle'>last</button> | " 405 + " <dropdown button-text='section...' :options='getSectionOptions(articleItem, section)' :button-style='btnStyle'></dropdown>" 406 + " <hr/>edit: <button @click.prevent='editOneLiner(articleItem)' class='dropbtn' :style='btnStyle'>one-liner...</button>" 407 + " <button @click.prevent='editAbstract(articleItem.article)' class='dropbtn' :style='btnStyle'>abstract...</button> | " 408 + " <button @click.prevent='remove(section, articleItem)' class='dropbtn' :style='btnStyle'>delete</button>" 409 + " </td>" 410 + " </tr>" 411 + " </table>" 412 + " </template>" 413 + "</div>" 414}) 415;
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.