├── README.md ├── CONTRIBUTING.md ├── LICENSE ├── index.css ├── spritz.html ├── style.css ├── index.html └── spritz.js /README.md: -------------------------------------------------------------------------------- 1 | # OpenSpritz is now [Glance](http://github.com/Miserlou/Glance) 2 | 3 | [Glance](http://github.com/Miserlou/Glance) is a news network for discerning speedreaders. 4 | 5 | Try it out at [http://glance.wtf](http://glance.wtf) or see the [code on Github](http://github.com/Miserlou/Glance). 6 | 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to OpenSpritz 2 | 3 | Please develop from the _dev_ branch, not the master branch! 4 | 5 | Please check for existing tickets before you open a new one. Please don't submit pull requests before opening a ticket 6 | and discussing. 7 | 8 | Because of the way that OpenSpritz is deployed, all pull requests must be sent to the _dev_ branch rather than the 9 | _master_ branch. Please also test your changes on a variety of sites, including CNN, the New York Times and The 10 | Guardian. 11 | 12 | Also please add yourself to the Contributors list in the README, if you're so inclined! 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Rich Jones 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | #container{ 2 | width: 50%; 3 | margin-left: 25%; 4 | margin-right: 25%; 5 | } 6 | 7 | /* This button was generated using CSSButtonGenerator.com */ 8 | .button { 9 | background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #ededed), color-stop(1, #dfdfdf) ); 10 | background:-moz-linear-gradient( center top, #ededed 5%, #dfdfdf 100% ); 11 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#dfdfdf'); 12 | background-color:#ededed; 13 | -webkit-border-top-left-radius:6px; 14 | -moz-border-radius-topleft:6px; 15 | border-top-left-radius:6px; 16 | -webkit-border-top-right-radius:6px; 17 | -moz-border-radius-topright:6px; 18 | border-top-right-radius:6px; 19 | -webkit-border-bottom-right-radius:6px; 20 | -moz-border-radius-bottomright:6px; 21 | border-bottom-right-radius:6px; 22 | -webkit-border-bottom-left-radius:6px; 23 | -moz-border-radius-bottomleft:6px; 24 | border-bottom-left-radius:6px; 25 | text-indent:0; 26 | border:1px solid #dcdcdc; 27 | display:inline-block; 28 | color:#777777; 29 | font-family:arial; 30 | font-size:12px; 31 | font-weight:bold; 32 | font-style:normal; 33 | height:38px; 34 | line-height:38px; 35 | width: auto; 36 | padding-left: 6px; 37 | padding-right: 6px; 38 | text-decoration:none; 39 | text-align:center; 40 | } 41 | 42 | .button a { 43 | color:#777777; 44 | text-decoration:none; 45 | } 46 | 47 | .button:hover { 48 | background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #dfdfdf), color-stop(1, #ededed) ); 49 | background:-moz-linear-gradient( center top, #dfdfdf 5%, #ededed 100% ); 50 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dfdfdf', endColorstr='#ededed'); 51 | background-color:#dfdfdf; 52 | }.button:active { 53 | position:relative; 54 | top:1px; 55 | } -------------------------------------------------------------------------------- /spritz.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 | 7 |
8 | ――――――――――ф――――――――――― 9 |
10 | 11 |
12 | Choose a WPM to start. 13 |
14 | 15 |
16 | ―――――――――――――――――――――― 17 |
18 | 19 | 37 | 38 | 39 | 40 | 41 | 42 | OpenSpritz 1.1 43 | 44 | 45 | [x] 46 | 47 | 48 |
49 |
50 | 51 |
52 |
53 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | margin: 0px; 3 | } 4 | 5 | #spritz_holder{ 6 | 7 | box-shadow: 2px 2px 4px rgba(0, 0, 0, .2); 8 | background-color: #ffffff; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | z-index: 99999999999999999; 13 | /* 600px+; small tablet portrait */ 14 | width: 100%; 15 | padding-top: 12px; 16 | padding-bottom: 12px; 17 | margin-bottom: 12px; 18 | min-height: 166px; 19 | } 20 | 21 | #spritz_spacer{ 22 | width: 100%; 23 | /* 600px+; small tablet portrait */ 24 | min-height: 210px; 25 | } 26 | 27 | #spritz_container{ 28 | background-color: #ffffff; 29 | /* 600px+; small tablet portrait */ 30 | width: 420px; 31 | margin-left: auto; 32 | margin-right: auto; 33 | line-height: 43px; 34 | } 35 | 36 | #spritz_result{ 37 | text-align: center; 38 | font-family: 'Droid Sans Mono', sans-serif; 39 | /* 600px+; small tablet portrait */ 40 | padding-top: 9px; 41 | padding-bottom: 9px; 42 | min-height: 40px; 43 | font-size: 32px; 44 | } 45 | 46 | #guide_top, #guide_bottom{ 47 | text-align: center; 48 | font-family: 'Droid Sans Mono', sans-serif; 49 | color: #dddddd; 50 | /* 600px+; small tablet portrait */ 51 | font-size: 32px; 52 | } 53 | 54 | #notch{ 55 | text-align: center; 56 | font-family: 'Droid Sans Mono', sans-serif; 57 | color: #dddddd; 58 | /* 600px+; small tablet portrait */ 59 | margin-top: 9px; 60 | padding-top: 12px; 61 | font-size: 32px; 62 | } 63 | 64 | #spritz_credits{ 65 | float: right; 66 | font-family: 'Droid Sans Mono', sans-serif; 67 | /* 600px+; small tablet portrait */ 68 | font-size: 13px; 69 | } 70 | 71 | #spritz_credits a{ 72 | color: #dddddd; 73 | text-decoration: none; 74 | } 75 | 76 | #spritz_selector, #spritz_toggle{ 77 | float: left; 78 | width: auto; 79 | } 80 | 81 | .invisible{ 82 | font-family: 'Droid Sans Mono', sans-serif; 83 | color: #ffffff; 84 | position: static; 85 | } 86 | 87 | .start, .pivot, .end, .spritz_start, .spritz_pivot, .spritz_end{ 88 | color: #333333; 89 | text-align: center; 90 | font-family: 'Droid Sans Mono', sans-serif; 91 | /* 600px+; small tablet portrait */ 92 | font-size: 32px; 93 | } 94 | .pivot, .spritz_pivot{ 95 | color: #de0000; 96 | } 97 | 98 | @media (max-width: 599px) { 99 | 100 | #spritz_holder{ 101 | width: 320 102 | padding-top: 4px; 103 | padding-bottom: 6px; 104 | margin-bottom: 6px; 105 | min-height: 70px; 106 | } 107 | 108 | #spritz_spacer{ 109 | min-height: 105px; 110 | } 111 | 112 | #spritz_container{ 113 | width: 320px; 114 | margin-left: auto; 115 | margin-right: auto; 116 | line-height: 22px; 117 | } 118 | 119 | #spritz_result{ 120 | min-height: 22px; 121 | font-size: 22px; 122 | padding-top: 11px; 123 | padding-bottom: 6px; 124 | } 125 | 126 | #guide_top, #guide_bottom{ 127 | font-size: 22px; 128 | } 129 | 130 | #notch{ 131 | margin-top: 6px; 132 | padding-top: 8px; 133 | font-size: 22px; 134 | } 135 | 136 | #spritz_credits{ 137 | font-size: 10px; 138 | } 139 | 140 | .start, .pivot, .end, .spritz_start, .spritz_pivot, .spritz_end{ 141 | font-size: 22px; 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OpenSpritz 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |

A Nice Cup of Tea

14 |

George Orwell, 12 January 1946

15 | 16 | 24 | 25 |
26 | 27 | Glance this! 28 | 29 |
30 | 31 |
32 | 33 | Glance this! (Remote Dev) 34 | 35 |
36 | 37 |
38 | 39 | Glance this! (Local) 40 | 41 |
42 | 43 |

44 | 45 |
46 | If you look up 'tea' in the first cookery book that comes to hand you will probably find that it is unmentioned; or at most you will find a few lines of sketchy instructions which give no ruling on several of the most important points. This is curious, not only because tea is one of the main stays of civilization in this country, as well as in Eire, Australia and New Zealand, but because the best manner of making it is the subject of violent disputes. When I look through my own recipe for the perfect cup of tea, I find no fewer than eleven outstanding points. On perhaps two of them there would be pretty general agreement, but at least four others are acutely controversial. Here are my own eleven rules, every one of which I regard as golden: First of all, one should use Indian or Ceylonese tea. China tea has virtues which are not to be despised nowadays — it is economical, and one can drink it without milk — but there is not much stimulation in it. One does not feel wiser, braver or more optimistic after drinking it. Anyone who has used that comforting phrase 'a nice cup of tea' invariably means Indian tea. Secondly, tea should be made in small quantities — that is, in a teapot. Tea out of an urn is always tasteless, while army tea, made in a cauldron, tastes of grease and whitewash. The teapot should be made of china or earthenware. Silver or Britanniaware teapots produce inferior tea and enamel pots are worse; though curiously enough a pewter teapot (a rarity nowadays) is not so bad. Thirdly, the pot should be warmed beforehand. This is better done by placing it on the hob than by the usual method of swilling it out with hot water. Fourthly, the tea should be strong. For a pot holding a quart, if you are going to fill it nearly to the brim, six heaped teaspoons would be about right. In a time of rationing, this is not an idea that can be realized on every day of the week, but I maintain that one strong cup of tea is better than twenty weak ones. All true tea lovers not only like their tea strong, but like it a little stronger with each year that passes — a fact which is recognized in the extra ration issued to old-age pensioners. Fifthly, the tea should be put straight into the pot. No strainers, muslin bags or other devices to imprison the tea. In some countries teapots are fitted with little dangling baskets under the spout to catch the stray leaves, which are supposed to be harmful. Actually one can swallow tea-leaves in considerable quantities without ill effect, and if the tea is not loose in the pot it never infuses properly. Sixthly, one should take the teapot to the kettle and not the other way about. The water should be actually boiling at the moment of impact, which means that one should keep it on the flame while one pours. Some people add that one should only use water that has been freshly brought to the boil, but I have never noticed that it makes any difference. Seventhly, after making the tea, one should stir it, or better, give the pot a good shake, afterwards allowing the leaves to settle. Eighthly, one should drink out of a good breakfast cup — that is, the cylindrical type of cup, not the flat, shallow type. The breakfast cup holds more, and with the other kind one's tea is always half cold before one has well started on it. Ninthly, one should pour the cream off the milk before using it for tea. Milk that is too creamy always gives tea a sickly taste. Tenthly, one should pour tea into the cup first. This is one of the most controversial points of all; indeed in every family in Britain there are probably two schools of thought on the subject. The milk-first school can bring forward some fairly strong arguments, but I maintain that my own argument is unanswerable. This is that, by putting the tea in first and stirring as one pours, one can exactly regulate the amount of milk whereas one is liable to put in too much milk if one does it the other way round. Lastly, tea — unless one is drinking it in the Russian style — should be drunk without sugar. I know very well that I am in a minority here. But still, how can you call yourself a true tealover if you destroy the flavour of your tea by putting sugar in it? It would be equally reasonable to put in pepper or salt. Tea is meant to be bitter, just as beer is meant to be bitter. If you sweeten it, you are no longer tasting the tea, you are merely tasting the sugar; you could make a very similar drink by dissolving sugar in plain hot water. Some people would answer that they don't like tea in itself, that they only drink it in order to be warmed and stimulated, and they need sugar to take the taste away. To those misguided people I would say: Try drinking tea without sugar for, say, a fortnight and it is very unlikely that you will ever want to ruin your tea by sweetening it again. These are not the only controversial points to arise in connexion with tea drinking, but they are sufficient to show how subtilized the whole business has become. There is also the mysterious social etiquette surrounding the teapot (why is it considered vulgar to drink out of your saucer, for instance?) and much might be written about the subsidiary uses of tealeaves, such as telling fortunes, predicting the arrival of visitors, feeding rabbits, healing burns and sweeping the carpet. It is worth paying attention to such details as warming the pot and using water that is really boiling, so as to make quite sure of wringing out of one's ration the twenty good, strong cups of that two ounces, properly handled, ought to represent. 47 |
48 | 49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /spritz.js: -------------------------------------------------------------------------------- 1 | // spritz.js 2 | // A JavaScript Speed Reader 3 | // rich@gun.io 4 | // https://github.com/Miserlou/OpenSpritz 5 | 6 | // Please don't abuse this. 7 | var readability_token = '172b057cd7cfccf27b60a36f16b1acde12783893'; 8 | var diffbot_token = '2efef432c72b5a923408e04353c39a7c'; 9 | 10 | function create_spritz(){ 11 | 12 | spritz_loader = function() { 13 | //getURL("https://rawgithub.com/Miserlou/OpenSpritz/master/spritz.html", function(data){ 14 | 15 | //getURL("https://rawgithub.com/Miserlou/OpenSpritz/dev/spritz.html", function(data){ 16 | 17 | // This won't work in Firefox because an old bug and won't work in Chrome because of security stuff: 18 | //getURL("spritz.html", function(data){ 19 | 20 | //getURL("https://rawgithub.com/Miserlou/OpenSpritz/dev/spritz.html", function(data){ 21 | 22 | // RawGit's CDN usage: 23 | // "Since files are not refreshed after the first request, 24 | // it's best to use a specific tag or commit URL, not a branch URL." 25 | getURL("https://cdn.rawgit.com/Miserlou/OpenSpritz/9e92c605032be16c986ed699d68e0acd3534e6b1/spritz.html", function(data){ 26 | var spritzContainer = document.getElementById("spritz_container"); 27 | 28 | if (!spritzContainer) { 29 | var ele = document.createElement("div"); 30 | data = data.replace(/(\r\n|\n|\r)/gm,""); 31 | ele.innerHTML = data; 32 | document.body.insertBefore(ele, document.body.firstChild); 33 | document.getElementById("spritz_toggle").style.display = "none"; 34 | }; 35 | 36 | document.getElementById("spritz_selector").addEventListener("change", function(e) { 37 | clearTimeouts(); 38 | spritz(); 39 | }); 40 | }); 41 | }; 42 | 43 | spritz_loader(); 44 | } 45 | 46 | function getURL(url, callback) { 47 | var xmlhttp = new XMLHttpRequest(); 48 | 49 | xmlhttp.onreadystatechange = function() { 50 | if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { 51 | callback(xmlhttp.responseText); 52 | } 53 | } 54 | 55 | xmlhttp.open("GET", url, true); 56 | xmlhttp.send(); 57 | } 58 | 59 | function hide_spritz(){ 60 | document.getElementById("spritz_spacer").style.display = "none"; 61 | document.getElementById("spritz_container").style.display = "none"; 62 | document.getElementById("spritz_holder").style.display = "none"; 63 | } 64 | 65 | // Entry point to the beef. 66 | // Gets the WPM and the selected text, if any. 67 | function spritz(){ 68 | 69 | var wpm = parseInt(document.getElementById("spritz_selector").value, 10); 70 | if(wpm < 1){ 71 | return; 72 | } 73 | 74 | var selection = getSelectionText(); 75 | if(selection){ 76 | spritzify(selection); 77 | } 78 | else{ 79 | spritzifyURL(); 80 | } 81 | } 82 | 83 | // The meat! 84 | function spritzify(input){ 85 | 86 | var wpm = parseInt(document.getElementById("spritz_selector").value, 10); 87 | var ms_per_word = 60000/wpm; 88 | 89 | // Split on any spaces. 90 | var all_words = input.split(/\s+/); 91 | 92 | // The reader won't stop if the selection starts or ends with spaces 93 | if (all_words[0] == "") 94 | { 95 | all_words = all_words.slice(1, all_words.length); 96 | } 97 | 98 | if (all_words[all_words.length - 1] == "") 99 | { 100 | all_words = all_words.slice(0, all_words.length - 1); 101 | } 102 | 103 | var word = ''; 104 | var result = ''; 105 | 106 | // Preprocess words 107 | var temp_words = all_words.slice(0); // copy Array 108 | var t = 0; 109 | 110 | for (var i=0; i 8) && all_words[i].indexOf('.') == -1){ 118 | temp_words.splice(t+1, 0, all_words[i]); 119 | temp_words.splice(t+1, 0, all_words[i]); 120 | t++; 121 | t++; 122 | } 123 | 124 | // Add an additional space after punctuation. 125 | if(all_words[i].indexOf('.') != -1 || all_words[i].indexOf('!') != -1 || all_words[i].indexOf('?') != -1 || all_words[i].indexOf(':') != -1 || all_words[i].indexOf(';') != -1|| all_words[i].indexOf(')') != -1){ 126 | temp_words.splice(t+1, 0, " "); 127 | temp_words.splice(t+1, 0, " "); 128 | temp_words.splice(t+1, 0, " "); 129 | t++; 130 | t++; 131 | t++; 132 | } 133 | 134 | t++; 135 | 136 | } 137 | 138 | all_words = temp_words.slice(0); 139 | 140 | var currentWord = 0; 141 | var running = true; 142 | var spritz_timers = new Array(); 143 | 144 | document.getElementById("spritz_toggle").addEventListener("click", function() { 145 | if(running) { 146 | stopSpritz(); 147 | } else { 148 | startSpritz(); 149 | } 150 | }); 151 | 152 | function updateValues(i) { 153 | 154 | var p = pivot(all_words[i]); 155 | document.getElementById("spritz_result").innerHTML = p; 156 | currentWord = i; 157 | 158 | } 159 | 160 | function startSpritz() { 161 | 162 | document.getElementById("spritz_toggle").style.display = "block"; 163 | document.getElementById("spritz_toggle").textContent = "Pause"; 164 | 165 | running = true; 166 | 167 | spritz_timers.push(setInterval(function() { 168 | updateValues(currentWord); 169 | currentWord++; 170 | if(currentWord >= all_words.length) { 171 | currentWord = 0; 172 | stopSpritz(); 173 | } 174 | }, ms_per_word)); 175 | } 176 | 177 | function stopSpritz() { 178 | for(var i = 0; i < spritz_timers.length; i++) { 179 | clearTimeout(spritz_timers[i]); 180 | } 181 | 182 | document.getElementById("spritz_toggle").textContent = "Play"; 183 | running = false; 184 | } 185 | 186 | startSpritz(); 187 | } 188 | 189 | // Find the red-character of the current word. 190 | function pivot(word){ 191 | var length = word.length; 192 | 193 | var bestLetter = 1; 194 | switch (length) { 195 | case 1: 196 | bestLetter = 1; // first 197 | break; 198 | case 2: 199 | case 3: 200 | case 4: 201 | case 5: 202 | bestLetter = 2; // second 203 | break; 204 | case 6: 205 | case 7: 206 | case 8: 207 | case 9: 208 | bestLetter = 3; // third 209 | break; 210 | case 10: 211 | case 11: 212 | case 12: 213 | case 13: 214 | bestLetter = 4; // fourth 215 | break; 216 | default: 217 | bestLetter = 5; // fifth 218 | }; 219 | 220 | word = decodeEntities(word); 221 | var start = '.'.repeat((11-bestLetter)) + word.slice(0, bestLetter-1).replace('.', '•'); 222 | var middle = word.slice(bestLetter-1,bestLetter).replace('.', '•'); 223 | var end = word.slice(bestLetter, length).replace('.', '•') + '.'.repeat((11-(word.length-bestLetter))); 224 | 225 | var result; 226 | result = "" + start; 227 | result = result + ""; 228 | result = result + middle; 229 | result = result + ""; 230 | result = result + end; 231 | result = result + ""; 232 | 233 | result = result.replace(/\./g, ""); 234 | 235 | return result; 236 | } 237 | 238 | // Get the currently selected text, if any. 239 | // Shameless pinched from StackOverflow. 240 | function getSelectionText() { 241 | var text = ""; 242 | if (typeof window.getSelection != "undefined") { 243 | var sel = window.getSelection(); 244 | if (sel.rangeCount) { 245 | var container = document.createElement("div"); 246 | for (var i = 0, len = sel.rangeCount; i < len; ++i) { 247 | container.appendChild(sel.getRangeAt(i).cloneContents()); 248 | } 249 | text = container.innerText || container.textContent; 250 | } 251 | } else if (typeof document.selection != "undefined") { 252 | if (document.selection.type == "Text") { 253 | text = document.selection.createRange().text; 254 | } 255 | } 256 | if(text === ""){ 257 | return false; 258 | } 259 | else{ 260 | return text; 261 | } 262 | } 263 | 264 | // Uses the Readability API to get the juicy content of the current page. 265 | function spritzifyURL(){ 266 | var url = document.URL; 267 | 268 | //getURL("https://www.readability.com/api/content/v1/parser?url="+ encodeURIComponent(url) +"&token=" + readability_token +"&callback=?", 269 | getURL("https://api.diffbot.com/v2/article?url="+ encodeURIComponent(url) +"&token=" + diffbot_token, // +"&callback=?", 270 | function(data) { 271 | 272 | data = JSON.parse(data); 273 | 274 | if(data.error){ 275 | document.getElementById("spritz_result").innerText = "Article extraction failed. Try selecting text instead."; 276 | return; 277 | } 278 | 279 | var title = ''; 280 | if(data.title !== ""){ 281 | title = data.title + ". "; 282 | } 283 | 284 | var author = ''; 285 | if(data.author !== undefined){ 286 | author = "By " + data.author + ". "; 287 | } 288 | 289 | var body = data.text; 290 | body = body.trim(); // Trim trailing and leading whitespace. 291 | body = body.replace(/\s+/g, ' '); // Shrink long whitespaces. 292 | 293 | var text_content = title + author + body; 294 | text_content = text_content.replace(/\./g, '. '); // Make sure punctuation is apprpriately spaced. 295 | text_content = text_content.replace(/\?/g, '? '); 296 | text_content = text_content.replace(/\!/g, '! '); 297 | spritzify(text_content); 298 | }); 299 | 300 | } 301 | 302 | ////// 303 | // Helpers 304 | ////// 305 | 306 | // This is a hack using the fact that browers sequentially id the timers. 307 | function clearTimeouts(){ 308 | var id = window.setTimeout(function() {}, 0); 309 | 310 | while (id--) { 311 | window.clearTimeout(id); 312 | } 313 | } 314 | 315 | // Let strings repeat themselves, 316 | // because JavaScript isn't as awesome as Python. 317 | String.prototype.repeat = function( num ){ 318 | if(num < 1){ 319 | return new Array( Math.abs(num) + 1 ).join( this ); 320 | } 321 | return new Array( num + 1 ).join( this ); 322 | }; 323 | 324 | function decodeEntities(s){ 325 | var str, temp= document.createElement('p'); 326 | temp.innerHTML= s; 327 | str= temp.textContent || temp.innerText; 328 | temp=null; 329 | return str; 330 | } 331 | 332 | 333 | --------------------------------------------------------------------------------