├── .DS_Store ├── LICENSE ├── README.md ├── css ├── .DS_Store ├── codemirror.css ├── pygment_trac.css └── stylesheet.css ├── img ├── .DS_Store ├── bg_hr.png ├── blacktocat.png ├── icon_download.png ├── image.png ├── logo.png ├── one.png ├── sprite_download.png ├── three.png └── two.png ├── index.html ├── lib ├── .DS_Store ├── codemirror.js ├── color-thief.js ├── css.js ├── filereader.js ├── inflection.js ├── jsbeautify │ ├── beautify-css.js │ ├── beautify-html.js │ └── beautify.js ├── ntc.js ├── template.js └── tinycolor.js └── src ├── aco2html.js └── index.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/.DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Web Semantics, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Color-Palette-Toolkit 2 | 3 | A lightweight Javascript library that reads the color values from a PhotoShop ACO file or an Image and turns them into a list of named colors using FileReader interface (http://bgrins.github.com/filereader.js/) and few other great Open Source projects. The Javascript code responsible of reading Photoshop ACO files has been ported from C program 4 | https://github.com/jasonm23/photoshop-aco2UIColors 5 | 6 | Try it in the [Live Demo](http://websemantics.github.io/Color-Palette-Toolkit) 7 | 8 | * Extract Color Pallete from Photoshop ACO file or an Image 9 | * Generate CSS and HTML code for each color 10 | * Generates human names for each color 11 | * Generates lighter and darker shades for each if required 12 | 13 | Get the color pallete from Adobe Photoshop ACO file 14 | 15 | ![alt text](https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/master/img/one.png "Color Palette") 16 | 17 | Generate an extra 'lighter' or 'darker' color of each 18 | 19 | ![alt text](https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/master/img/two.png "Color Palette") 20 | 21 | Generate an extra 'lighter' and 'darker' color of each 22 | 23 | ![alt text](https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/master/img/three.png "Color Palette") 24 | 25 | Get the color pallete from an Image file 26 | 27 | ![alt text](https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/master/img/image.png "Color Palette") 28 | 29 | 30 | ## License 31 | Color-Palette-Toolkit is open source under MIT License. See LICENSE. 32 | 33 | ## Open Source projects used 34 | * jQuery - http://jquery.com/ 35 | * filereader.js - http://github.com/bgrins/filereader.js 36 | * ntc js (Name that Color JavaScript) - http://chir.ag/projects/ntc/ 37 | * TinyColor - https://github.com/bgrins/TinyColor 38 | * JavaScript Inflection - https://github.com/simonwex/inflection-js 39 | * TemplateEngine - http://krasimirtsonev.com/blog/article/Javascript-template-engine-in-just-20-line 40 | * CodeMirror - http://codemirror.net/mode/css/ 41 | * js-beautify - https://github.com/beautify-web/js-beautify 42 | * Flattastic Pro Color Palette - http://codepen.io/devi8/pen/lvIeh (https://dribbble.com/devi8) 43 | * Flattastic Pro Color Palette - https://dribbble.com/shots/1186718-Flattastic-Pro-Color-Palette 44 | * Color Thief - https://github.com/lokesh/color-thief 45 | * photoshop-aco2UIColors - https://github.com/jasonm23/photoshop-aco2UIColors 46 | -------------------------------------------------------------------------------- /css/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/css/.DS_Store -------------------------------------------------------------------------------- /css/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | } 8 | 9 | /* PADDING */ 10 | 11 | .CodeMirror-lines { 12 | padding: 4px 0; /* Vertical padding around content */ 13 | } 14 | .CodeMirror pre { 15 | padding: 0 4px; /* Horizontal padding of content */ 16 | } 17 | 18 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 19 | background-color: white; /* The little square between H and V scrollbars */ 20 | } 21 | 22 | /* GUTTER */ 23 | 24 | .CodeMirror-gutters { 25 | border-right: 1px solid #ddd; 26 | background-color: #f7f7f7; 27 | white-space: nowrap; 28 | } 29 | .CodeMirror-linenumbers {} 30 | .CodeMirror-linenumber { 31 | padding: 0 3px 0 5px; 32 | min-width: 20px; 33 | text-align: right; 34 | color: #999; 35 | -moz-box-sizing: content-box; 36 | box-sizing: content-box; 37 | } 38 | 39 | .CodeMirror-guttermarker { color: black; } 40 | .CodeMirror-guttermarker-subtle { color: #999; } 41 | 42 | /* CURSOR */ 43 | 44 | .CodeMirror div.CodeMirror-cursor { 45 | border-left: 1px solid black; 46 | } 47 | /* Shown when moving in bi-directional text */ 48 | .CodeMirror div.CodeMirror-secondarycursor { 49 | border-left: 1px solid silver; 50 | } 51 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursor { 52 | width: auto; 53 | border: 0; 54 | background: #7e7; 55 | } 56 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursors { 57 | z-index: 1; 58 | } 59 | 60 | .cm-animate-fat-cursor { 61 | width: auto; 62 | border: 0; 63 | -webkit-animation: blink 1.06s steps(1) infinite; 64 | -moz-animation: blink 1.06s steps(1) infinite; 65 | animation: blink 1.06s steps(1) infinite; 66 | } 67 | @-moz-keyframes blink { 68 | 0% { background: #7e7; } 69 | 50% { background: none; } 70 | 100% { background: #7e7; } 71 | } 72 | @-webkit-keyframes blink { 73 | 0% { background: #7e7; } 74 | 50% { background: none; } 75 | 100% { background: #7e7; } 76 | } 77 | @keyframes blink { 78 | 0% { background: #7e7; } 79 | 50% { background: none; } 80 | 100% { background: #7e7; } 81 | } 82 | 83 | /* Can style cursor different in overwrite (non-insert) mode */ 84 | div.CodeMirror-overwrite div.CodeMirror-cursor {} 85 | 86 | .cm-tab { display: inline-block; text-decoration: inherit; } 87 | 88 | .CodeMirror-ruler { 89 | border-left: 1px solid #ccc; 90 | position: absolute; 91 | } 92 | 93 | /* DEFAULT THEME */ 94 | 95 | .cm-s-default .cm-keyword {color: #708;} 96 | .cm-s-default .cm-atom {color: #219;} 97 | .cm-s-default .cm-number {color: #164;} 98 | .cm-s-default .cm-def {color: #00f;} 99 | .cm-s-default .cm-variable, 100 | .cm-s-default .cm-punctuation, 101 | .cm-s-default .cm-property, 102 | .cm-s-default .cm-operator {} 103 | .cm-s-default .cm-variable-2 {color: #05a;} 104 | .cm-s-default .cm-variable-3 {color: #085;} 105 | .cm-s-default .cm-comment {color: #a50;} 106 | .cm-s-default .cm-string {color: #a11;} 107 | .cm-s-default .cm-string-2 {color: #f50;} 108 | .cm-s-default .cm-meta {color: #555;} 109 | .cm-s-default .cm-qualifier {color: #555;} 110 | .cm-s-default .cm-builtin {color: #30a;} 111 | .cm-s-default .cm-bracket {color: #997;} 112 | .cm-s-default .cm-tag {color: #170;} 113 | .cm-s-default .cm-attribute {color: #00c;} 114 | .cm-s-default .cm-header {color: blue;} 115 | .cm-s-default .cm-quote {color: #090;} 116 | .cm-s-default .cm-hr {color: #999;} 117 | .cm-s-default .cm-link {color: #00c;} 118 | 119 | .cm-negative {color: #d44;} 120 | .cm-positive {color: #292;} 121 | .cm-header, .cm-strong {font-weight: bold;} 122 | .cm-em {font-style: italic;} 123 | .cm-link {text-decoration: underline;} 124 | .cm-strikethrough {text-decoration: line-through;} 125 | 126 | .cm-s-default .cm-error {color: #f00;} 127 | .cm-invalidchar {color: #f00;} 128 | 129 | /* Default styles for common addons */ 130 | 131 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 132 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 133 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 134 | .CodeMirror-activeline-background {background: #e8f2ff;} 135 | 136 | /* STOP */ 137 | 138 | /* The rest of this file contains styles related to the mechanics of 139 | the editor. You probably shouldn't touch them. */ 140 | 141 | .CodeMirror { 142 | line-height: 1; 143 | position: relative; 144 | overflow: hidden; 145 | background: white; 146 | color: black; 147 | } 148 | 149 | .CodeMirror-scroll { 150 | overflow: scroll !important; /* Things will break if this is overridden */ 151 | /* 30px is the magic margin used to hide the element's real scrollbars */ 152 | /* See overflow: hidden in .CodeMirror */ 153 | margin-bottom: -30px; margin-right: -30px; 154 | padding-bottom: 30px; 155 | height: 100%; 156 | outline: none; /* Prevent dragging from highlighting the element */ 157 | position: relative; 158 | -moz-box-sizing: content-box; 159 | box-sizing: content-box; 160 | } 161 | .CodeMirror-sizer { 162 | position: relative; 163 | border-right: 30px solid transparent; 164 | -moz-box-sizing: content-box; 165 | box-sizing: content-box; 166 | } 167 | 168 | /* The fake, visible scrollbars. Used to force redraw during scrolling 169 | before actuall scrolling happens, thus preventing shaking and 170 | flickering artifacts. */ 171 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 172 | position: absolute; 173 | z-index: 6; 174 | display: none; 175 | } 176 | .CodeMirror-vscrollbar { 177 | right: 0; top: 0; 178 | overflow-x: hidden; 179 | overflow-y: scroll; 180 | } 181 | .CodeMirror-hscrollbar { 182 | bottom: 0; left: 0; 183 | overflow-y: hidden; 184 | overflow-x: scroll; 185 | } 186 | .CodeMirror-scrollbar-filler { 187 | right: 0; bottom: 0; 188 | } 189 | .CodeMirror-gutter-filler { 190 | left: 0; bottom: 0; 191 | } 192 | 193 | .CodeMirror-gutters { 194 | position: absolute; left: 0; top: 0; 195 | z-index: 3; 196 | } 197 | .CodeMirror-gutter { 198 | white-space: normal; 199 | height: 100%; 200 | -moz-box-sizing: content-box; 201 | box-sizing: content-box; 202 | display: inline-block; 203 | margin-bottom: -30px; 204 | /* Hack to make IE7 behave */ 205 | *zoom:1; 206 | *display:inline; 207 | } 208 | .CodeMirror-gutter-wrapper { 209 | position: absolute; 210 | z-index: 4; 211 | height: 100%; 212 | } 213 | .CodeMirror-gutter-elt { 214 | position: absolute; 215 | cursor: default; 216 | z-index: 4; 217 | } 218 | 219 | .CodeMirror-lines { 220 | cursor: text; 221 | min-height: 1px; /* prevents collapsing before first draw */ 222 | } 223 | .CodeMirror pre { 224 | /* Reset some styles that the rest of the page might have set */ 225 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 226 | border-width: 0; 227 | background: transparent; 228 | font-family: inherit; 229 | font-size: inherit; 230 | margin: 0; 231 | white-space: pre; 232 | word-wrap: normal; 233 | line-height: inherit; 234 | color: inherit; 235 | z-index: 2; 236 | position: relative; 237 | overflow: visible; 238 | } 239 | .CodeMirror-wrap pre { 240 | word-wrap: break-word; 241 | white-space: pre-wrap; 242 | word-break: normal; 243 | } 244 | 245 | .CodeMirror-linebackground { 246 | position: absolute; 247 | left: 0; right: 0; top: 0; bottom: 0; 248 | z-index: 0; 249 | } 250 | 251 | .CodeMirror-linewidget { 252 | position: relative; 253 | z-index: 2; 254 | overflow: auto; 255 | } 256 | 257 | .CodeMirror-widget {} 258 | 259 | .CodeMirror-measure { 260 | position: absolute; 261 | width: 100%; 262 | height: 0; 263 | overflow: hidden; 264 | visibility: hidden; 265 | } 266 | .CodeMirror-measure pre { position: static; } 267 | 268 | .CodeMirror div.CodeMirror-cursor { 269 | position: absolute; 270 | border-right: none; 271 | width: 0; 272 | } 273 | 274 | div.CodeMirror-cursors { 275 | visibility: hidden; 276 | position: relative; 277 | z-index: 3; 278 | } 279 | .CodeMirror-focused div.CodeMirror-cursors { 280 | visibility: visible; 281 | } 282 | 283 | .CodeMirror-selected { background: #d9d9d9; } 284 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 285 | .CodeMirror-crosshair { cursor: crosshair; } 286 | 287 | .cm-searching { 288 | background: #ffa; 289 | background: rgba(255, 255, 0, .4); 290 | } 291 | 292 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 293 | .CodeMirror span { *vertical-align: text-bottom; } 294 | 295 | /* Used to force a border model for a node */ 296 | .cm-force-border { padding-right: .1px; } 297 | 298 | @media print { 299 | /* Hide the cursor when printing */ 300 | .CodeMirror div.CodeMirror-cursors { 301 | visibility: hidden; 302 | } 303 | } 304 | 305 | /* See issue #2901 */ 306 | .cm-tab-wrap-hack:after { content: ''; } 307 | 308 | /* Help users use markselection to safely style text background */ 309 | span.CodeMirror-selectedtext { background: none; } -------------------------------------------------------------------------------- /css/pygment_trac.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #f0f3f3; } 3 | .highlight .c { color: #0099FF; font-style: italic } /* Comment */ 4 | .highlight .err { color: #AA0000; background-color: #FFAAAA } /* Error */ 5 | .highlight .k { color: #006699; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #555555 } /* Operator */ 7 | .highlight .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #009999 } /* Comment.Preproc */ 9 | .highlight .c1 { color: #0099FF; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */ 11 | .highlight .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 14 | .highlight .gh { color: #003300; font-weight: bold } /* Generic.Heading */ 15 | .highlight .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */ 16 | .highlight .go { color: #AAAAAA } /* Generic.Output */ 17 | .highlight .gp { color: #000099; font-weight: bold } /* Generic.Prompt */ 18 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 19 | .highlight .gu { color: #003300; font-weight: bold } /* Generic.Subheading */ 20 | .highlight .gt { color: #99CC66 } /* Generic.Traceback */ 21 | .highlight .kc { color: #006699; font-weight: bold } /* Keyword.Constant */ 22 | .highlight .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */ 23 | .highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */ 24 | .highlight .kp { color: #006699 } /* Keyword.Pseudo */ 25 | .highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */ 27 | .highlight .m { color: #FF6600 } /* Literal.Number */ 28 | .highlight .s { color: #CC3300 } /* Literal.String */ 29 | .highlight .na { color: #330099 } /* Name.Attribute */ 30 | .highlight .nb { color: #336666 } /* Name.Builtin */ 31 | .highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #336600 } /* Name.Constant */ 33 | .highlight .nd { color: #9999FF } /* Name.Decorator */ 34 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ 35 | .highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */ 36 | .highlight .nf { color: #CC00FF } /* Name.Function */ 37 | .highlight .nl { color: #9999FF } /* Name.Label */ 38 | .highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */ 39 | .highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */ 40 | .highlight .nv { color: #003333 } /* Name.Variable */ 41 | .highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ 42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 43 | .highlight .mf { color: #FF6600 } /* Literal.Number.Float */ 44 | .highlight .mh { color: #FF6600 } /* Literal.Number.Hex */ 45 | .highlight .mi { color: #FF6600 } /* Literal.Number.Integer */ 46 | .highlight .mo { color: #FF6600 } /* Literal.Number.Oct */ 47 | .highlight .sb { color: #CC3300 } /* Literal.String.Backtick */ 48 | .highlight .sc { color: #CC3300 } /* Literal.String.Char */ 49 | .highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */ 50 | .highlight .s2 { color: #CC3300 } /* Literal.String.Double */ 51 | .highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */ 52 | .highlight .sh { color: #CC3300 } /* Literal.String.Heredoc */ 53 | .highlight .si { color: #AA0000 } /* Literal.String.Interpol */ 54 | .highlight .sx { color: #CC3300 } /* Literal.String.Other */ 55 | .highlight .sr { color: #33AAAA } /* Literal.String.Regex */ 56 | .highlight .s1 { color: #CC3300 } /* Literal.String.Single */ 57 | .highlight .ss { color: #FFCC33 } /* Literal.String.Symbol */ 58 | .highlight .bp { color: #336666 } /* Name.Builtin.Pseudo */ 59 | .highlight .vc { color: #003333 } /* Name.Variable.Class */ 60 | .highlight .vg { color: #003333 } /* Name.Variable.Global */ 61 | .highlight .vi { color: #003333 } /* Name.Variable.Instance */ 62 | .highlight .il { color: #FF6600 } /* Literal.Number.Integer.Long */ 63 | 64 | .type-csharp .highlight .k { color: #0000FF } 65 | .type-csharp .highlight .kt { color: #0000FF } 66 | .type-csharp .highlight .nf { color: #000000; font-weight: normal } 67 | .type-csharp .highlight .nc { color: #2B91AF } 68 | .type-csharp .highlight .nn { color: #000000 } 69 | .type-csharp .highlight .s { color: #A31515 } 70 | .type-csharp .highlight .sc { color: #A31515 } 71 | -------------------------------------------------------------------------------- /css/stylesheet.css: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Slate Theme for GitHub Pages 3 | by Jason Costello, @jsncostello 4 | *******************************************************************************/ 5 | 6 | @import url(pygment_trac.css); 7 | 8 | /******************************************************************************* 9 | MeyerWeb Reset 10 | *******************************************************************************/ 11 | 12 | html, body, div, span, applet, object, iframe, 13 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 14 | a, abbr, acronym, address, big, cite, code, 15 | del, dfn, em, img, ins, kbd, q, s, samp, 16 | small, strike, strong, sub, sup, tt, var, 17 | b, u, i, center, 18 | dl, dt, dd, ol, ul, li, 19 | fieldset, form, label, legend, 20 | table, caption, tbody, tfoot, thead, tr, th, td, 21 | article, aside, canvas, details, embed, 22 | figure, figcaption, footer, header, hgroup, 23 | menu, nav, output, ruby, section, summary, 24 | time, mark, audio, video { 25 | margin: 0; 26 | padding: 0; 27 | border: 0; 28 | font: inherit; 29 | vertical-align: baseline; 30 | } 31 | 32 | /* HTML5 display-role reset for older browsers */ 33 | article, aside, details, figcaption, figure, 34 | footer, header, hgroup, menu, nav, section { 35 | display: block; 36 | } 37 | 38 | ol, ul { 39 | list-style: none; 40 | } 41 | 42 | table { 43 | border-collapse: collapse; 44 | border-spacing: 0; 45 | } 46 | 47 | /******************************************************************************* 48 | Theme Styles 49 | *******************************************************************************/ 50 | 51 | body { 52 | box-sizing: border-box; 53 | color:#373737; 54 | background: #212121; 55 | font-size: 16px; 56 | font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif; 57 | line-height: 1.5; 58 | -webkit-font-smoothing: antialiased; 59 | } 60 | 61 | h1, h2, h3, h4, h5, h6 { 62 | margin: 10px 0; 63 | font-weight: 700; 64 | color:#222222; 65 | font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif; 66 | letter-spacing: -1px; 67 | } 68 | 69 | h1 { 70 | font-size: 36px; 71 | font-weight: 700; 72 | } 73 | 74 | h2 { 75 | padding-bottom: 10px; 76 | font-size: 32px; 77 | background: url('../img/bg_hr.png') repeat-x bottom; 78 | } 79 | 80 | h3 { 81 | font-size: 24px; 82 | } 83 | 84 | h4 { 85 | font-size: 21px; 86 | } 87 | 88 | h5 { 89 | font-size: 18px; 90 | } 91 | 92 | h6 { 93 | font-size: 16px; 94 | } 95 | 96 | p { 97 | margin: 10px 0 15px 0; 98 | } 99 | 100 | footer p { 101 | color: #f2f2f2; 102 | } 103 | 104 | a { 105 | text-decoration: none; 106 | color: #007edf; 107 | text-shadow: none; 108 | 109 | transition: color 0.5s ease; 110 | transition: text-shadow 0.5s ease; 111 | -webkit-transition: color 0.5s ease; 112 | -webkit-transition: text-shadow 0.5s ease; 113 | -moz-transition: color 0.5s ease; 114 | -moz-transition: text-shadow 0.5s ease; 115 | -o-transition: color 0.5s ease; 116 | -o-transition: text-shadow 0.5s ease; 117 | -ms-transition: color 0.5s ease; 118 | -ms-transition: text-shadow 0.5s ease; 119 | } 120 | 121 | a:hover, a:focus {text-decoration: underline;} 122 | 123 | footer a { 124 | color: #F2F2F2; 125 | text-decoration: underline; 126 | } 127 | 128 | em { 129 | font-style: italic; 130 | } 131 | 132 | strong { 133 | font-weight: bold; 134 | } 135 | 136 | img { 137 | position: relative; 138 | margin: 0 auto; 139 | max-width: 739px; 140 | padding: 5px; 141 | margin: 10px 0 10px 0; 142 | border: 1px solid #ebebeb; 143 | /* 144 | box-shadow: 0 0 5px #ebebeb; 145 | -webkit-box-shadow: 0 0 5px #ebebeb; 146 | -moz-box-shadow: 0 0 5px #ebebeb; 147 | -o-box-shadow: 0 0 5px #ebebeb; 148 | -ms-box-shadow: 0 0 5px #ebebeb;*/ 149 | } 150 | 151 | p img { 152 | display: inline; 153 | margin: 0; 154 | padding: 0; 155 | vertical-align: middle; 156 | text-align: center; 157 | border: none; 158 | } 159 | 160 | pre, code { 161 | width: 100%; 162 | color: #222; 163 | background-color: #fff; 164 | 165 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; 166 | font-size: 14px; 167 | 168 | border-radius: 2px; 169 | -moz-border-radius: 2px; 170 | -webkit-border-radius: 2px; 171 | } 172 | 173 | pre { 174 | width: 100%; 175 | padding: 10px; 176 | box-shadow: 0 0 10px rgba(0,0,0,.1); 177 | overflow: auto; 178 | } 179 | 180 | code { 181 | padding: 3px; 182 | margin: 0 3px; 183 | box-shadow: 0 0 10px rgba(0,0,0,.1); 184 | } 185 | 186 | pre code { 187 | display: block; 188 | box-shadow: none; 189 | } 190 | 191 | blockquote { 192 | color: #666; 193 | margin-bottom: 20px; 194 | padding: 0 0 0 20px; 195 | border-left: 3px solid #bbb; 196 | } 197 | 198 | 199 | ul, ol, dl { 200 | margin-bottom: 15px 201 | } 202 | 203 | ul { 204 | list-style-position: inside; 205 | list-style: disc; 206 | padding-left: 20px; 207 | } 208 | 209 | ol { 210 | list-style-position: inside; 211 | list-style: decimal; 212 | padding-left: 20px; 213 | } 214 | 215 | dl dt { 216 | font-weight: bold; 217 | } 218 | 219 | dl dd { 220 | padding-left: 20px; 221 | font-style: italic; 222 | } 223 | 224 | dl p { 225 | padding-left: 20px; 226 | font-style: italic; 227 | } 228 | 229 | hr { 230 | height: 1px; 231 | margin-bottom: 5px; 232 | border: none; 233 | background: url('../img/bg_hr.png') repeat-x center; 234 | } 235 | 236 | table { 237 | border: 1px solid #373737; 238 | margin-bottom: 20px; 239 | text-align: left; 240 | } 241 | 242 | th { 243 | font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif; 244 | padding: 10px; 245 | background: #373737; 246 | color: #fff; 247 | } 248 | 249 | td { 250 | padding: 10px; 251 | border: 1px solid #373737; 252 | } 253 | 254 | form { 255 | background: #f2f2f2; 256 | padding: 20px; 257 | } 258 | 259 | /******************************************************************************* 260 | Full-Width Styles 261 | *******************************************************************************/ 262 | 263 | .outer { 264 | width: 100%; 265 | } 266 | 267 | .inner { 268 | position: relative; 269 | max-width: 640px; 270 | padding: 20px 10px; 271 | margin: 0 auto; 272 | } 273 | 274 | #forkme_banner { 275 | display: block; 276 | position: absolute; 277 | top:0; 278 | right: 10px; 279 | z-index: 10; 280 | padding: 10px 50px 10px 10px; 281 | color: #fff; 282 | background: url('../img/blacktocat.png') #0090ff no-repeat 95% 50%; 283 | font-weight: 700; 284 | box-shadow: 0 0 10px rgba(0,0,0,.5); 285 | border-bottom-left-radius: 2px; 286 | border-bottom-right-radius: 2px; 287 | } 288 | 289 | #header_wrap { 290 | background: -moz-linear-gradient(top, #373737, #212121); 291 | background: -webkit-linear-gradient(top, #373737, #212121); 292 | background: -ms-linear-gradient(top, #373737, #212121); 293 | background: -o-linear-gradient(top, #373737, #212121); 294 | background: linear-gradient(top, #373737, #212121); 295 | } 296 | 297 | #header_wrap .inner { 298 | padding: 50px 10px 30px 10px; 299 | background: url(../img/logo.png) no-repeat; 300 | } 301 | 302 | 303 | 304 | 305 | 306 | #project_title { 307 | margin: 0; 308 | color: #fff; 309 | font-size: 42px; 310 | font-weight: 700; 311 | text-shadow: #111 0px 0px 10px; 312 | } 313 | 314 | #project_tagline { 315 | color: #fff; 316 | font-size: 24px; 317 | font-weight: 300; 318 | background: none; 319 | text-shadow: #111 0px 0px 10px; 320 | } 321 | 322 | #downloads { 323 | position: absolute; 324 | width: 210px; 325 | z-index: 10; 326 | bottom: -40px; 327 | right: 0; 328 | height: 70px; 329 | background: url('../img/icon_download.png') no-repeat 0% 90%; 330 | } 331 | 332 | .zip_download_link { 333 | display: block; 334 | float: right; 335 | width: 90px; 336 | height:70px; 337 | text-indent: -5000px; 338 | overflow: hidden; 339 | background: url(../img/sprite_download.png) no-repeat bottom left; 340 | } 341 | 342 | .tar_download_link { 343 | display: block; 344 | float: right; 345 | width: 90px; 346 | height:70px; 347 | text-indent: -5000px; 348 | overflow: hidden; 349 | background: url(../img/sprite_download.png) no-repeat bottom right; 350 | margin-left: 10px; 351 | } 352 | 353 | .zip_download_link:hover { 354 | background: url(../img/sprite_download.png) no-repeat top left; 355 | } 356 | 357 | .tar_download_link:hover { 358 | background: url(../img/sprite_download.png) no-repeat top right; 359 | } 360 | 361 | #main_content_wrap { 362 | background: #f2f2f2; 363 | border-top: 1px solid #111; 364 | border-bottom: 1px solid #111; 365 | } 366 | 367 | #main_content { 368 | padding-top: 40px; 369 | } 370 | 371 | #footer_wrap { 372 | background: #212121; 373 | } 374 | 375 | 376 | 377 | /******************************************************************************* 378 | Small Device Styles 379 | *******************************************************************************/ 380 | 381 | @media screen and (max-width: 480px) { 382 | body { 383 | font-size:14px; 384 | } 385 | 386 | #downloads { 387 | display: none; 388 | } 389 | 390 | .inner { 391 | min-width: 320px; 392 | max-width: 480px; 393 | } 394 | 395 | #project_title { 396 | font-size: 32px; 397 | } 398 | 399 | h1 { 400 | font-size: 28px; 401 | } 402 | 403 | h2 { 404 | font-size: 24px; 405 | } 406 | 407 | h3 { 408 | font-size: 21px; 409 | } 410 | 411 | h4 { 412 | font-size: 18px; 413 | } 414 | 415 | h5 { 416 | font-size: 14px; 417 | } 418 | 419 | h6 { 420 | font-size: 12px; 421 | } 422 | 423 | code, pre { 424 | min-width: 320px; 425 | max-width: 480px; 426 | font-size: 11px; 427 | } 428 | 429 | } 430 | 431 | 432 | /* Custom page CSS 433 | -------------------------------------------------- */ 434 | /* Not required for template or sticky footer method. */ 435 | 436 | div.result { 437 | border-radius: 5px; 438 | background-color: red; 439 | padding:0px 15px; 440 | margin-bottom: 10px; 441 | } 442 | 443 | form { 444 | width: 100%; 445 | padding: 10px 20px; 446 | box-shadow: 0 0 10px rgba(0,0,0,.1); 447 | overflow: auto; 448 | background-color:#ffffff; 449 | margin-bottom: 20px; 450 | } 451 | 452 | a { 453 | } 454 | a:link { 455 | color: #1982d1; 456 | text-decoration: none; 457 | } 458 | 459 | a:active { 460 | color: #B73045; 461 | text-decoration: none; 462 | } 463 | 464 | a:visited { 465 | color: #1982d1; 466 | text-decoration: none; 467 | } 468 | a:hover { 469 | color: #B73045; 470 | text-decoration: none; 471 | } 472 | 473 | .hidden { 474 | display: none; 475 | } 476 | 477 | #image img{ 478 | width:150px; 479 | } -------------------------------------------------------------------------------- /img/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/img/.DS_Store -------------------------------------------------------------------------------- /img/bg_hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/img/bg_hr.png -------------------------------------------------------------------------------- /img/blacktocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/img/blacktocat.png -------------------------------------------------------------------------------- /img/icon_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/img/icon_download.png -------------------------------------------------------------------------------- /img/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/img/image.png -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/img/logo.png -------------------------------------------------------------------------------- /img/one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/img/one.png -------------------------------------------------------------------------------- /img/sprite_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/img/sprite_download.png -------------------------------------------------------------------------------- /img/three.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/img/three.png -------------------------------------------------------------------------------- /img/two.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/img/two.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Color Palette Toolkit : WebSemantics 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 | View on GitHub 37 | 38 |

Color Palette Toolkit

39 |

A lightweight Javascript library that reads the color values from a PhotoShop ACO file or an Image and turns them into a list of named colors.

40 | 41 |
42 | Download this project as a .zip file 43 | Download this project as a tar.gz file 44 |
45 |
46 |
47 | 48 | 49 |
50 |
51 | 52 |

53 | How To Use

54 | 55 |

Load the ACO / Image file from your local drive and get a list of the Palette Colors as CSS HEX colors. HTML code will also be generated and a human name given to each color.

56 | 57 | 58 |
59 |
60 |
61 | clear 62 | 63 | 67 |    68 | 69 | 75 |    76 | 77 | 90 |
91 |

92 | 93 | 94 | 95 |

96 |
97 |
98 | 99 |
100 | 101 |
102 | 103 |
104 | 105 |

106 |
107 | Support or Contact

108 | 109 |

Having trouble with Pages? Contact adnan@websemantics.ca and we’ll help you sort it out.

110 |
111 |
112 | 113 | 114 | 122 | 123 | 124 | 125 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /lib/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/lib/.DS_Store -------------------------------------------------------------------------------- /lib/color-thief.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Color Thief v2.0 3 | * by Lokesh Dhakar - http://www.lokeshdhakar.com 4 | * 5 | * License 6 | * ------- 7 | * Creative Commons Attribution 2.5 License: 8 | * http://creativecommons.org/licenses/by/2.5/ 9 | * 10 | * Thanks 11 | * ------ 12 | * Nick Rabinowitz - For creating quantize.js. 13 | * John Schulz - For clean up and optimization. @JFSIII 14 | * Nathan Spady - For adding drag and drop support to the demo page. 15 | * 16 | */ 17 | 18 | 19 | /* 20 | CanvasImage Class 21 | Class that wraps the html image element and canvas. 22 | It also simplifies some of the canvas context manipulation 23 | with a set of helper functions. 24 | */ 25 | var CanvasImage = function (image) { 26 | this.canvas = document.createElement('canvas'); 27 | this.context = this.canvas.getContext('2d'); 28 | 29 | document.body.appendChild(this.canvas); 30 | 31 | this.width = this.canvas.width = image.width; 32 | this.height = this.canvas.height = image.height; 33 | 34 | this.context.drawImage(image, 0, 0, this.width, this.height); 35 | }; 36 | 37 | CanvasImage.prototype.clear = function () { 38 | this.context.clearRect(0, 0, this.width, this.height); 39 | }; 40 | 41 | CanvasImage.prototype.update = function (imageData) { 42 | this.context.putImageData(imageData, 0, 0); 43 | }; 44 | 45 | CanvasImage.prototype.getPixelCount = function () { 46 | return this.width * this.height; 47 | }; 48 | 49 | CanvasImage.prototype.getImageData = function () { 50 | return this.context.getImageData(0, 0, this.width, this.height); 51 | }; 52 | 53 | CanvasImage.prototype.removeCanvas = function () { 54 | this.canvas.parentNode.removeChild(this.canvas); 55 | }; 56 | 57 | 58 | var ColorThief = function () {}; 59 | 60 | /* 61 | * getColor(sourceImage[, quality]) 62 | * returns {r: num, g: num, b: num} 63 | * 64 | * Use the median cut algorithm provided by quantize.js to cluster similar 65 | * colors and return the base color from the largest cluster. 66 | * 67 | * Quality is an optional argument. It needs to be an integer. 0 is the highest quality settings. 68 | * 10 is the default. There is a trade-off between quality and speed. The bigger the number, the 69 | * faster a color will be returned but the greater the likelihood that it will not be the visually 70 | * most dominant color. 71 | * 72 | * */ 73 | ColorThief.prototype.getColor = function(sourceImage, quality) { 74 | var palette = this.getPalette(sourceImage, 5, quality); 75 | var dominantColor = palette[0]; 76 | return dominantColor; 77 | }; 78 | 79 | 80 | /* 81 | * getPalette(sourceImage[, colorCount, quality]) 82 | * returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...] 83 | * 84 | * Use the median cut algorithm provided by quantize.js to cluster similar colors. 85 | * 86 | * colorCount determines the size of the palette; the number of colors returned. If not set, it 87 | * defaults to 10. 88 | * 89 | * BUGGY: Function does not always return the requested amount of colors. It can be +/- 2. 90 | * 91 | * quality is an optional argument. It needs to be an integer. 0 is the highest quality settings. 92 | * 10 is the default. There is a trade-off between quality and speed. The bigger the number, the 93 | * faster the palette generation but the greater the likelihood that colors will be missed. 94 | * 95 | * 96 | */ 97 | ColorThief.prototype.getPalette = function(sourceImage, colorCount, quality) { 98 | 99 | if (typeof colorCount === 'undefined') { 100 | colorCount = 10; 101 | } 102 | if (typeof quality === 'undefined') { 103 | quality = 10; 104 | } 105 | 106 | // Create custom CanvasImage object 107 | var image = new CanvasImage(sourceImage); 108 | var imageData = image.getImageData(); 109 | var pixels = imageData.data; 110 | var pixelCount = image.getPixelCount(); 111 | 112 | // Store the RGB values in an array format suitable for quantize function 113 | var pixelArray = []; 114 | for (var i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) { 115 | offset = i * 4; 116 | r = pixels[offset + 0]; 117 | g = pixels[offset + 1]; 118 | b = pixels[offset + 2]; 119 | a = pixels[offset + 3]; 120 | // If pixel is mostly opaque and not white 121 | if (a >= 125) { 122 | if (!(r > 250 && g > 250 && b > 250)) { 123 | pixelArray.push([r, g, b]); 124 | } 125 | } 126 | } 127 | 128 | // Send array to quantize function which clusters values 129 | // using median cut algorithm 130 | var cmap = MMCQ.quantize(pixelArray, colorCount); 131 | var palette = cmap.palette(); 132 | 133 | // Clean up 134 | image.removeCanvas(); 135 | 136 | return palette; 137 | }; 138 | 139 | 140 | 141 | 142 | /*! 143 | * quantize.js Copyright 2008 Nick Rabinowitz. 144 | * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 145 | */ 146 | 147 | // fill out a couple protovis dependencies 148 | /*! 149 | * Block below copied from Protovis: http://mbostock.github.com/protovis/ 150 | * Copyright 2010 Stanford Visualization Group 151 | * Licensed under the BSD License: http://www.opensource.org/licenses/bsd-license.php 152 | */ 153 | if (!pv) { 154 | var pv = { 155 | map: function(array, f) { 156 | var o = {}; 157 | return f ? array.map(function(d, i) { o.index = i; return f.call(o, d); }) : array.slice(); 158 | }, 159 | naturalOrder: function(a, b) { 160 | return (a < b) ? -1 : ((a > b) ? 1 : 0); 161 | }, 162 | sum: function(array, f) { 163 | var o = {}; 164 | return array.reduce(f ? function(p, d, i) { o.index = i; return p + f.call(o, d); } : function(p, d) { return p + d; }, 0); 165 | }, 166 | max: function(array, f) { 167 | return Math.max.apply(null, f ? pv.map(array, f) : array); 168 | } 169 | }; 170 | } 171 | 172 | 173 | 174 | /** 175 | * Basic Javascript port of the MMCQ (modified median cut quantization) 176 | * algorithm from the Leptonica library (http://www.leptonica.com/). 177 | * Returns a color map you can use to map original pixels to the reduced 178 | * palette. Still a work in progress. 179 | * 180 | * @author Nick Rabinowitz 181 | * @example 182 | 183 | // array of pixels as [R,G,B] arrays 184 | var myPixels = [[190,197,190], [202,204,200], [207,214,210], [211,214,211], [205,207,207] 185 | // etc 186 | ]; 187 | var maxColors = 4; 188 | 189 | var cmap = MMCQ.quantize(myPixels, maxColors); 190 | var newPalette = cmap.palette(); 191 | var newPixels = myPixels.map(function(p) { 192 | return cmap.map(p); 193 | }); 194 | 195 | */ 196 | var MMCQ = (function() { 197 | // private constants 198 | var sigbits = 5, 199 | rshift = 8 - sigbits, 200 | maxIterations = 1000, 201 | fractByPopulations = 0.75; 202 | 203 | // get reduced-space color index for a pixel 204 | function getColorIndex(r, g, b) { 205 | return (r << (2 * sigbits)) + (g << sigbits) + b; 206 | } 207 | 208 | // Simple priority queue 209 | function PQueue(comparator) { 210 | var contents = [], 211 | sorted = false; 212 | 213 | function sort() { 214 | contents.sort(comparator); 215 | sorted = true; 216 | } 217 | 218 | return { 219 | push: function(o) { 220 | contents.push(o); 221 | sorted = false; 222 | }, 223 | peek: function(index) { 224 | if (!sorted) sort(); 225 | if (index===undefined) index = contents.length - 1; 226 | return contents[index]; 227 | }, 228 | pop: function() { 229 | if (!sorted) sort(); 230 | return contents.pop(); 231 | }, 232 | size: function() { 233 | return contents.length; 234 | }, 235 | map: function(f) { 236 | return contents.map(f); 237 | }, 238 | debug: function() { 239 | if (!sorted) sort(); 240 | return contents; 241 | } 242 | }; 243 | } 244 | 245 | // 3d color space box 246 | function VBox(r1, r2, g1, g2, b1, b2, histo) { 247 | var vbox = this; 248 | vbox.r1 = r1; 249 | vbox.r2 = r2; 250 | vbox.g1 = g1; 251 | vbox.g2 = g2; 252 | vbox.b1 = b1; 253 | vbox.b2 = b2; 254 | vbox.histo = histo; 255 | } 256 | VBox.prototype = { 257 | volume: function(force) { 258 | var vbox = this; 259 | if (!vbox._volume || force) { 260 | vbox._volume = ((vbox.r2 - vbox.r1 + 1) * (vbox.g2 - vbox.g1 + 1) * (vbox.b2 - vbox.b1 + 1)); 261 | } 262 | return vbox._volume; 263 | }, 264 | count: function(force) { 265 | var vbox = this, 266 | histo = vbox.histo; 267 | if (!vbox._count_set || force) { 268 | var npix = 0, 269 | i, j, k; 270 | for (i = vbox.r1; i <= vbox.r2; i++) { 271 | for (j = vbox.g1; j <= vbox.g2; j++) { 272 | for (k = vbox.b1; k <= vbox.b2; k++) { 273 | index = getColorIndex(i,j,k); 274 | npix += (histo[index] || 0); 275 | } 276 | } 277 | } 278 | vbox._count = npix; 279 | vbox._count_set = true; 280 | } 281 | return vbox._count; 282 | }, 283 | copy: function() { 284 | var vbox = this; 285 | return new VBox(vbox.r1, vbox.r2, vbox.g1, vbox.g2, vbox.b1, vbox.b2, vbox.histo); 286 | }, 287 | avg: function(force) { 288 | var vbox = this, 289 | histo = vbox.histo; 290 | if (!vbox._avg || force) { 291 | var ntot = 0, 292 | mult = 1 << (8 - sigbits), 293 | rsum = 0, 294 | gsum = 0, 295 | bsum = 0, 296 | hval, 297 | i, j, k, histoindex; 298 | for (i = vbox.r1; i <= vbox.r2; i++) { 299 | for (j = vbox.g1; j <= vbox.g2; j++) { 300 | for (k = vbox.b1; k <= vbox.b2; k++) { 301 | histoindex = getColorIndex(i,j,k); 302 | hval = histo[histoindex] || 0; 303 | ntot += hval; 304 | rsum += (hval * (i + 0.5) * mult); 305 | gsum += (hval * (j + 0.5) * mult); 306 | bsum += (hval * (k + 0.5) * mult); 307 | } 308 | } 309 | } 310 | if (ntot) { 311 | vbox._avg = [~~(rsum/ntot), ~~(gsum/ntot), ~~(bsum/ntot)]; 312 | } else { 313 | // console.log('empty box'); 314 | vbox._avg = [ 315 | ~~(mult * (vbox.r1 + vbox.r2 + 1) / 2), 316 | ~~(mult * (vbox.g1 + vbox.g2 + 1) / 2), 317 | ~~(mult * (vbox.b1 + vbox.b2 + 1) / 2) 318 | ]; 319 | } 320 | } 321 | return vbox._avg; 322 | }, 323 | contains: function(pixel) { 324 | var vbox = this, 325 | rval = pixel[0] >> rshift; 326 | gval = pixel[1] >> rshift; 327 | bval = pixel[2] >> rshift; 328 | return (rval >= vbox.r1 && rval <= vbox.r2 && 329 | gval >= vbox.g1 && gval <= vbox.g2 && 330 | bval >= vbox.b1 && bval <= vbox.b2); 331 | } 332 | }; 333 | 334 | // Color map 335 | function CMap() { 336 | this.vboxes = new PQueue(function(a,b) { 337 | return pv.naturalOrder( 338 | a.vbox.count()*a.vbox.volume(), 339 | b.vbox.count()*b.vbox.volume() 340 | ); 341 | }); 342 | } 343 | CMap.prototype = { 344 | push: function(vbox) { 345 | this.vboxes.push({ 346 | vbox: vbox, 347 | color: vbox.avg() 348 | }); 349 | }, 350 | palette: function() { 351 | return this.vboxes.map(function(vb) { return vb.color; }); 352 | }, 353 | size: function() { 354 | return this.vboxes.size(); 355 | }, 356 | map: function(color) { 357 | var vboxes = this.vboxes; 358 | for (var i=0; i 251 392 | var idx = vboxes.length-1, 393 | highest = vboxes[idx].color; 394 | if (highest[0] > 251 && highest[1] > 251 && highest[2] > 251) 395 | vboxes[idx].color = [255,255,255]; 396 | } 397 | }; 398 | 399 | // histo (1-d array, giving the number of pixels in 400 | // each quantized region of color space), or null on error 401 | function getHisto(pixels) { 402 | var histosize = 1 << (3 * sigbits), 403 | histo = new Array(histosize), 404 | index, rval, gval, bval; 405 | pixels.forEach(function(pixel) { 406 | rval = pixel[0] >> rshift; 407 | gval = pixel[1] >> rshift; 408 | bval = pixel[2] >> rshift; 409 | index = getColorIndex(rval, gval, bval); 410 | histo[index] = (histo[index] || 0) + 1; 411 | }); 412 | return histo; 413 | } 414 | 415 | function vboxFromPixels(pixels, histo) { 416 | var rmin=1000000, rmax=0, 417 | gmin=1000000, gmax=0, 418 | bmin=1000000, bmax=0, 419 | rval, gval, bval; 420 | // find min/max 421 | pixels.forEach(function(pixel) { 422 | rval = pixel[0] >> rshift; 423 | gval = pixel[1] >> rshift; 424 | bval = pixel[2] >> rshift; 425 | if (rval < rmin) rmin = rval; 426 | else if (rval > rmax) rmax = rval; 427 | if (gval < gmin) gmin = gval; 428 | else if (gval > gmax) gmax = gval; 429 | if (bval < bmin) bmin = bval; 430 | else if (bval > bmax) bmax = bval; 431 | }); 432 | return new VBox(rmin, rmax, gmin, gmax, bmin, bmax, histo); 433 | } 434 | 435 | function medianCutApply(histo, vbox) { 436 | if (!vbox.count()) return; 437 | 438 | var rw = vbox.r2 - vbox.r1 + 1, 439 | gw = vbox.g2 - vbox.g1 + 1, 440 | bw = vbox.b2 - vbox.b1 + 1, 441 | maxw = pv.max([rw, gw, bw]); 442 | // only one pixel, no split 443 | if (vbox.count() == 1) { 444 | return [vbox.copy()]; 445 | } 446 | /* Find the partial sum arrays along the selected axis. */ 447 | var total = 0, 448 | partialsum = [], 449 | lookaheadsum = [], 450 | i, j, k, sum, index; 451 | if (maxw == rw) { 452 | for (i = vbox.r1; i <= vbox.r2; i++) { 453 | sum = 0; 454 | for (j = vbox.g1; j <= vbox.g2; j++) { 455 | for (k = vbox.b1; k <= vbox.b2; k++) { 456 | index = getColorIndex(i,j,k); 457 | sum += (histo[index] || 0); 458 | } 459 | } 460 | total += sum; 461 | partialsum[i] = total; 462 | } 463 | } 464 | else if (maxw == gw) { 465 | for (i = vbox.g1; i <= vbox.g2; i++) { 466 | sum = 0; 467 | for (j = vbox.r1; j <= vbox.r2; j++) { 468 | for (k = vbox.b1; k <= vbox.b2; k++) { 469 | index = getColorIndex(j,i,k); 470 | sum += (histo[index] || 0); 471 | } 472 | } 473 | total += sum; 474 | partialsum[i] = total; 475 | } 476 | } 477 | else { /* maxw == bw */ 478 | for (i = vbox.b1; i <= vbox.b2; i++) { 479 | sum = 0; 480 | for (j = vbox.r1; j <= vbox.r2; j++) { 481 | for (k = vbox.g1; k <= vbox.g2; k++) { 482 | index = getColorIndex(j,k,i); 483 | sum += (histo[index] || 0); 484 | } 485 | } 486 | total += sum; 487 | partialsum[i] = total; 488 | } 489 | } 490 | partialsum.forEach(function(d,i) { 491 | lookaheadsum[i] = total-d; 492 | }); 493 | function doCut(color) { 494 | var dim1 = color + '1', 495 | dim2 = color + '2', 496 | left, right, vbox1, vbox2, d2, count2=0; 497 | for (i = vbox[dim1]; i <= vbox[dim2]; i++) { 498 | if (partialsum[i] > total / 2) { 499 | vbox1 = vbox.copy(); 500 | vbox2 = vbox.copy(); 501 | left = i - vbox[dim1]; 502 | right = vbox[dim2] - i; 503 | if (left <= right) 504 | d2 = Math.min(vbox[dim2] - 1, ~~(i + right / 2)); 505 | else d2 = Math.max(vbox[dim1], ~~(i - 1 - left / 2)); 506 | // avoid 0-count boxes 507 | while (!partialsum[d2]) d2++; 508 | count2 = lookaheadsum[d2]; 509 | while (!count2 && partialsum[d2-1]) count2 = lookaheadsum[--d2]; 510 | // set dimensions 511 | vbox1[dim2] = d2; 512 | vbox2[dim1] = vbox1[dim2] + 1; 513 | // console.log('vbox counts:', vbox.count(), vbox1.count(), vbox2.count()); 514 | return [vbox1, vbox2]; 515 | } 516 | } 517 | 518 | } 519 | // determine the cut planes 520 | return maxw == rw ? doCut('r') : 521 | maxw == gw ? doCut('g') : 522 | doCut('b'); 523 | } 524 | 525 | function quantize(pixels, maxcolors) { 526 | // short-circuit 527 | if (!pixels.length || maxcolors < 2 || maxcolors > 256) { 528 | // console.log('wrong number of maxcolors'); 529 | return false; 530 | } 531 | 532 | // XXX: check color content and convert to grayscale if insufficient 533 | 534 | var histo = getHisto(pixels), 535 | histosize = 1 << (3 * sigbits); 536 | 537 | // check that we aren't below maxcolors already 538 | var nColors = 0; 539 | histo.forEach(function() { nColors++; }); 540 | if (nColors <= maxcolors) { 541 | // XXX: generate the new colors from the histo and return 542 | } 543 | 544 | // get the beginning vbox from the colors 545 | var vbox = vboxFromPixels(pixels, histo), 546 | pq = new PQueue(function(a,b) { return pv.naturalOrder(a.count(), b.count()); }); 547 | pq.push(vbox); 548 | 549 | // inner function to do the iteration 550 | function iter(lh, target) { 551 | var ncolors = 1, 552 | niters = 0, 553 | vbox; 554 | while (niters < maxIterations) { 555 | vbox = lh.pop(); 556 | if (!vbox.count()) { /* just put it back */ 557 | lh.push(vbox); 558 | niters++; 559 | continue; 560 | } 561 | // do the cut 562 | var vboxes = medianCutApply(histo, vbox), 563 | vbox1 = vboxes[0], 564 | vbox2 = vboxes[1]; 565 | 566 | if (!vbox1) { 567 | // console.log("vbox1 not defined; shouldn't happen!"); 568 | return; 569 | } 570 | lh.push(vbox1); 571 | if (vbox2) { /* vbox2 can be null */ 572 | lh.push(vbox2); 573 | ncolors++; 574 | } 575 | if (ncolors >= target) return; 576 | if (niters++ > maxIterations) { 577 | // console.log("infinite loop; perhaps too few pixels!"); 578 | return; 579 | } 580 | } 581 | } 582 | 583 | // first set of colors, sorted by population 584 | iter(pq, fractByPopulations * maxcolors); 585 | 586 | // Re-sort by the product of pixel occupancy times the size in color space. 587 | var pq2 = new PQueue(function(a,b) { 588 | return pv.naturalOrder(a.count()*a.volume(), b.count()*b.volume()); 589 | }); 590 | while (pq.size()) { 591 | pq2.push(pq.pop()); 592 | } 593 | 594 | // next set - generate the median cuts using the (npix * vol) sorting. 595 | iter(pq2, maxcolors - pq2.size()); 596 | 597 | // calculate the actual colors 598 | var cmap = new CMap(); 599 | while (pq2.size()) { 600 | cmap.push(pq2.pop()); 601 | } 602 | 603 | return cmap; 604 | } 605 | 606 | return { 607 | quantize: quantize 608 | }; 609 | })(); -------------------------------------------------------------------------------- /lib/css.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineMode("css", function(config, parserConfig) { 15 | if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css"); 16 | 17 | var indentUnit = config.indentUnit, 18 | tokenHooks = parserConfig.tokenHooks, 19 | mediaTypes = parserConfig.mediaTypes || {}, 20 | mediaFeatures = parserConfig.mediaFeatures || {}, 21 | propertyKeywords = parserConfig.propertyKeywords || {}, 22 | nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {}, 23 | colorKeywords = parserConfig.colorKeywords || {}, 24 | valueKeywords = parserConfig.valueKeywords || {}, 25 | fontProperties = parserConfig.fontProperties || {}, 26 | allowNested = parserConfig.allowNested; 27 | 28 | var type, override; 29 | function ret(style, tp) { type = tp; return style; } 30 | 31 | // Tokenizers 32 | 33 | function tokenBase(stream, state) { 34 | var ch = stream.next(); 35 | if (tokenHooks[ch]) { 36 | var result = tokenHooks[ch](stream, state); 37 | if (result !== false) return result; 38 | } 39 | if (ch == "@") { 40 | stream.eatWhile(/[\w\\\-]/); 41 | return ret("def", stream.current()); 42 | } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) { 43 | return ret(null, "compare"); 44 | } else if (ch == "\"" || ch == "'") { 45 | state.tokenize = tokenString(ch); 46 | return state.tokenize(stream, state); 47 | } else if (ch == "#") { 48 | stream.eatWhile(/[\w\\\-]/); 49 | return ret("atom", "hash"); 50 | } else if (ch == "!") { 51 | stream.match(/^\s*\w*/); 52 | return ret("keyword", "important"); 53 | } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) { 54 | stream.eatWhile(/[\w.%]/); 55 | return ret("number", "unit"); 56 | } else if (ch === "-") { 57 | if (/[\d.]/.test(stream.peek())) { 58 | stream.eatWhile(/[\w.%]/); 59 | return ret("number", "unit"); 60 | } else if (stream.match(/^\w+-/)) { 61 | return ret("meta", "meta"); 62 | } 63 | } else if (/[,+>*\/]/.test(ch)) { 64 | return ret(null, "select-op"); 65 | } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { 66 | return ret("qualifier", "qualifier"); 67 | } else if (/[:;{}\[\]\(\)]/.test(ch)) { 68 | return ret(null, ch); 69 | } else if (ch == "u" && stream.match("rl(")) { 70 | stream.backUp(1); 71 | state.tokenize = tokenParenthesized; 72 | return ret("property", "word"); 73 | } else if (/[\w\\\-]/.test(ch)) { 74 | stream.eatWhile(/[\w\\\-]/); 75 | return ret("property", "word"); 76 | } else { 77 | return ret(null, null); 78 | } 79 | } 80 | 81 | function tokenString(quote) { 82 | return function(stream, state) { 83 | var escaped = false, ch; 84 | while ((ch = stream.next()) != null) { 85 | if (ch == quote && !escaped) { 86 | if (quote == ")") stream.backUp(1); 87 | break; 88 | } 89 | escaped = !escaped && ch == "\\"; 90 | } 91 | if (ch == quote || !escaped && quote != ")") state.tokenize = null; 92 | return ret("string", "string"); 93 | }; 94 | } 95 | 96 | function tokenParenthesized(stream, state) { 97 | stream.next(); // Must be '(' 98 | if (!stream.match(/\s*[\"\')]/, false)) 99 | state.tokenize = tokenString(")"); 100 | else 101 | state.tokenize = null; 102 | return ret(null, "("); 103 | } 104 | 105 | // Context management 106 | 107 | function Context(type, indent, prev) { 108 | this.type = type; 109 | this.indent = indent; 110 | this.prev = prev; 111 | } 112 | 113 | function pushContext(state, stream, type) { 114 | state.context = new Context(type, stream.indentation() + indentUnit, state.context); 115 | return type; 116 | } 117 | 118 | function popContext(state) { 119 | state.context = state.context.prev; 120 | return state.context.type; 121 | } 122 | 123 | function pass(type, stream, state) { 124 | return states[state.context.type](type, stream, state); 125 | } 126 | function popAndPass(type, stream, state, n) { 127 | for (var i = n || 1; i > 0; i--) 128 | state.context = state.context.prev; 129 | return pass(type, stream, state); 130 | } 131 | 132 | // Parser 133 | 134 | function wordAsValue(stream) { 135 | var word = stream.current().toLowerCase(); 136 | if (valueKeywords.hasOwnProperty(word)) 137 | override = "atom"; 138 | else if (colorKeywords.hasOwnProperty(word)) 139 | override = "keyword"; 140 | else 141 | override = "variable"; 142 | } 143 | 144 | var states = {}; 145 | 146 | states.top = function(type, stream, state) { 147 | if (type == "{") { 148 | return pushContext(state, stream, "block"); 149 | } else if (type == "}" && state.context.prev) { 150 | return popContext(state); 151 | } else if (type == "@media") { 152 | return pushContext(state, stream, "media"); 153 | } else if (type == "@font-face") { 154 | return "font_face_before"; 155 | } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) { 156 | return "keyframes"; 157 | } else if (type && type.charAt(0) == "@") { 158 | return pushContext(state, stream, "at"); 159 | } else if (type == "hash") { 160 | override = "builtin"; 161 | } else if (type == "word") { 162 | override = "tag"; 163 | } else if (type == "variable-definition") { 164 | return "maybeprop"; 165 | } else if (type == "interpolation") { 166 | return pushContext(state, stream, "interpolation"); 167 | } else if (type == ":") { 168 | return "pseudo"; 169 | } else if (allowNested && type == "(") { 170 | return pushContext(state, stream, "parens"); 171 | } 172 | return state.context.type; 173 | }; 174 | 175 | states.block = function(type, stream, state) { 176 | if (type == "word") { 177 | var word = stream.current().toLowerCase(); 178 | if (propertyKeywords.hasOwnProperty(word)) { 179 | override = "property"; 180 | return "maybeprop"; 181 | } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) { 182 | override = "string-2"; 183 | return "maybeprop"; 184 | } else if (allowNested) { 185 | override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag"; 186 | return "block"; 187 | } else { 188 | override += " error"; 189 | return "maybeprop"; 190 | } 191 | } else if (type == "meta") { 192 | return "block"; 193 | } else if (!allowNested && (type == "hash" || type == "qualifier")) { 194 | override = "error"; 195 | return "block"; 196 | } else { 197 | return states.top(type, stream, state); 198 | } 199 | }; 200 | 201 | states.maybeprop = function(type, stream, state) { 202 | if (type == ":") return pushContext(state, stream, "prop"); 203 | return pass(type, stream, state); 204 | }; 205 | 206 | states.prop = function(type, stream, state) { 207 | if (type == ";") return popContext(state); 208 | if (type == "{" && allowNested) return pushContext(state, stream, "propBlock"); 209 | if (type == "}" || type == "{") return popAndPass(type, stream, state); 210 | if (type == "(") return pushContext(state, stream, "parens"); 211 | 212 | if (type == "hash" && !/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { 213 | override += " error"; 214 | } else if (type == "word") { 215 | wordAsValue(stream); 216 | } else if (type == "interpolation") { 217 | return pushContext(state, stream, "interpolation"); 218 | } 219 | return "prop"; 220 | }; 221 | 222 | states.propBlock = function(type, _stream, state) { 223 | if (type == "}") return popContext(state); 224 | if (type == "word") { override = "property"; return "maybeprop"; } 225 | return state.context.type; 226 | }; 227 | 228 | states.parens = function(type, stream, state) { 229 | if (type == "{" || type == "}") return popAndPass(type, stream, state); 230 | if (type == ")") return popContext(state); 231 | if (type == "(") return pushContext(state, stream, "parens"); 232 | if (type == "word") wordAsValue(stream); 233 | return "parens"; 234 | }; 235 | 236 | states.pseudo = function(type, stream, state) { 237 | if (type == "word") { 238 | override = "variable-3"; 239 | return state.context.type; 240 | } 241 | return pass(type, stream, state); 242 | }; 243 | 244 | states.media = function(type, stream, state) { 245 | if (type == "(") return pushContext(state, stream, "media_parens"); 246 | if (type == "}") return popAndPass(type, stream, state); 247 | if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top"); 248 | 249 | if (type == "word") { 250 | var word = stream.current().toLowerCase(); 251 | if (word == "only" || word == "not" || word == "and") 252 | override = "keyword"; 253 | else if (mediaTypes.hasOwnProperty(word)) 254 | override = "attribute"; 255 | else if (mediaFeatures.hasOwnProperty(word)) 256 | override = "property"; 257 | else 258 | override = "error"; 259 | } 260 | return state.context.type; 261 | }; 262 | 263 | states.media_parens = function(type, stream, state) { 264 | if (type == ")") return popContext(state); 265 | if (type == "{" || type == "}") return popAndPass(type, stream, state, 2); 266 | return states.media(type, stream, state); 267 | }; 268 | 269 | states.font_face_before = function(type, stream, state) { 270 | if (type == "{") 271 | return pushContext(state, stream, "font_face"); 272 | return pass(type, stream, state); 273 | }; 274 | 275 | states.font_face = function(type, stream, state) { 276 | if (type == "}") return popContext(state); 277 | if (type == "word") { 278 | if (!fontProperties.hasOwnProperty(stream.current().toLowerCase())) 279 | override = "error"; 280 | else 281 | override = "property"; 282 | return "maybeprop"; 283 | } 284 | return "font_face"; 285 | }; 286 | 287 | states.keyframes = function(type, stream, state) { 288 | if (type == "word") { override = "variable"; return "keyframes"; } 289 | if (type == "{") return pushContext(state, stream, "top"); 290 | return pass(type, stream, state); 291 | }; 292 | 293 | states.at = function(type, stream, state) { 294 | if (type == ";") return popContext(state); 295 | if (type == "{" || type == "}") return popAndPass(type, stream, state); 296 | if (type == "word") override = "tag"; 297 | else if (type == "hash") override = "builtin"; 298 | return "at"; 299 | }; 300 | 301 | states.interpolation = function(type, stream, state) { 302 | if (type == "}") return popContext(state); 303 | if (type == "{" || type == ";") return popAndPass(type, stream, state); 304 | if (type != "variable") override = "error"; 305 | return "interpolation"; 306 | }; 307 | 308 | return { 309 | startState: function(base) { 310 | return {tokenize: null, 311 | state: "top", 312 | context: new Context("top", base || 0, null)}; 313 | }, 314 | 315 | token: function(stream, state) { 316 | if (!state.tokenize && stream.eatSpace()) return null; 317 | var style = (state.tokenize || tokenBase)(stream, state); 318 | if (style && typeof style == "object") { 319 | type = style[1]; 320 | style = style[0]; 321 | } 322 | override = style; 323 | state.state = states[state.state](type, stream, state); 324 | return override; 325 | }, 326 | 327 | indent: function(state, textAfter) { 328 | var cx = state.context, ch = textAfter && textAfter.charAt(0); 329 | var indent = cx.indent; 330 | if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev; 331 | if (cx.prev && 332 | (ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "font_face") || 333 | ch == ")" && (cx.type == "parens" || cx.type == "media_parens") || 334 | ch == "{" && (cx.type == "at" || cx.type == "media"))) { 335 | indent = cx.indent - indentUnit; 336 | cx = cx.prev; 337 | } 338 | return indent; 339 | }, 340 | 341 | electricChars: "}", 342 | blockCommentStart: "/*", 343 | blockCommentEnd: "*/", 344 | fold: "brace" 345 | }; 346 | }); 347 | 348 | function keySet(array) { 349 | var keys = {}; 350 | for (var i = 0; i < array.length; ++i) { 351 | keys[array[i]] = true; 352 | } 353 | return keys; 354 | } 355 | 356 | var mediaTypes_ = [ 357 | "all", "aural", "braille", "handheld", "print", "projection", "screen", 358 | "tty", "tv", "embossed" 359 | ], mediaTypes = keySet(mediaTypes_); 360 | 361 | var mediaFeatures_ = [ 362 | "width", "min-width", "max-width", "height", "min-height", "max-height", 363 | "device-width", "min-device-width", "max-device-width", "device-height", 364 | "min-device-height", "max-device-height", "aspect-ratio", 365 | "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio", 366 | "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color", 367 | "max-color", "color-index", "min-color-index", "max-color-index", 368 | "monochrome", "min-monochrome", "max-monochrome", "resolution", 369 | "min-resolution", "max-resolution", "scan", "grid" 370 | ], mediaFeatures = keySet(mediaFeatures_); 371 | 372 | var propertyKeywords_ = [ 373 | "align-content", "align-items", "align-self", "alignment-adjust", 374 | "alignment-baseline", "anchor-point", "animation", "animation-delay", 375 | "animation-direction", "animation-duration", "animation-fill-mode", 376 | "animation-iteration-count", "animation-name", "animation-play-state", 377 | "animation-timing-function", "appearance", "azimuth", "backface-visibility", 378 | "background", "background-attachment", "background-clip", "background-color", 379 | "background-image", "background-origin", "background-position", 380 | "background-repeat", "background-size", "baseline-shift", "binding", 381 | "bleed", "bookmark-label", "bookmark-level", "bookmark-state", 382 | "bookmark-target", "border", "border-bottom", "border-bottom-color", 383 | "border-bottom-left-radius", "border-bottom-right-radius", 384 | "border-bottom-style", "border-bottom-width", "border-collapse", 385 | "border-color", "border-image", "border-image-outset", 386 | "border-image-repeat", "border-image-slice", "border-image-source", 387 | "border-image-width", "border-left", "border-left-color", 388 | "border-left-style", "border-left-width", "border-radius", "border-right", 389 | "border-right-color", "border-right-style", "border-right-width", 390 | "border-spacing", "border-style", "border-top", "border-top-color", 391 | "border-top-left-radius", "border-top-right-radius", "border-top-style", 392 | "border-top-width", "border-width", "bottom", "box-decoration-break", 393 | "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", 394 | "caption-side", "clear", "clip", "color", "color-profile", "column-count", 395 | "column-fill", "column-gap", "column-rule", "column-rule-color", 396 | "column-rule-style", "column-rule-width", "column-span", "column-width", 397 | "columns", "content", "counter-increment", "counter-reset", "crop", "cue", 398 | "cue-after", "cue-before", "cursor", "direction", "display", 399 | "dominant-baseline", "drop-initial-after-adjust", 400 | "drop-initial-after-align", "drop-initial-before-adjust", 401 | "drop-initial-before-align", "drop-initial-size", "drop-initial-value", 402 | "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", 403 | "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", 404 | "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings", 405 | "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust", 406 | "font-stretch", "font-style", "font-synthesis", "font-variant", 407 | "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", 408 | "font-variant-ligatures", "font-variant-numeric", "font-variant-position", 409 | "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", 410 | "grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end", 411 | "grid-column-start", "grid-row", "grid-row-end", "grid-row-start", 412 | "grid-template", "grid-template-areas", "grid-template-columns", 413 | "grid-template-rows", "hanging-punctuation", "height", "hyphens", 414 | "icon", "image-orientation", "image-rendering", "image-resolution", 415 | "inline-box-align", "justify-content", "left", "letter-spacing", 416 | "line-break", "line-height", "line-stacking", "line-stacking-ruby", 417 | "line-stacking-shift", "line-stacking-strategy", "list-style", 418 | "list-style-image", "list-style-position", "list-style-type", "margin", 419 | "margin-bottom", "margin-left", "margin-right", "margin-top", 420 | "marker-offset", "marks", "marquee-direction", "marquee-loop", 421 | "marquee-play-count", "marquee-speed", "marquee-style", "max-height", 422 | "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", 423 | "nav-left", "nav-right", "nav-up", "object-fit", "object-position", 424 | "opacity", "order", "orphans", "outline", 425 | "outline-color", "outline-offset", "outline-style", "outline-width", 426 | "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", 427 | "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", 428 | "page", "page-break-after", "page-break-before", "page-break-inside", 429 | "page-policy", "pause", "pause-after", "pause-before", "perspective", 430 | "perspective-origin", "pitch", "pitch-range", "play-during", "position", 431 | "presentation-level", "punctuation-trim", "quotes", "region-break-after", 432 | "region-break-before", "region-break-inside", "region-fragment", 433 | "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", 434 | "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", 435 | "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin", 436 | "shape-outside", "size", "speak", "speak-as", "speak-header", 437 | "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", 438 | "tab-size", "table-layout", "target", "target-name", "target-new", 439 | "target-position", "text-align", "text-align-last", "text-decoration", 440 | "text-decoration-color", "text-decoration-line", "text-decoration-skip", 441 | "text-decoration-style", "text-emphasis", "text-emphasis-color", 442 | "text-emphasis-position", "text-emphasis-style", "text-height", 443 | "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", 444 | "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position", 445 | "text-wrap", "top", "transform", "transform-origin", "transform-style", 446 | "transition", "transition-delay", "transition-duration", 447 | "transition-property", "transition-timing-function", "unicode-bidi", 448 | "vertical-align", "visibility", "voice-balance", "voice-duration", 449 | "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", 450 | "voice-volume", "volume", "white-space", "widows", "width", "word-break", 451 | "word-spacing", "word-wrap", "z-index", 452 | // SVG-specific 453 | "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", 454 | "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events", 455 | "color-interpolation", "color-interpolation-filters", 456 | "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering", 457 | "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke", 458 | "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", 459 | "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering", 460 | "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal", 461 | "glyph-orientation-vertical", "text-anchor", "writing-mode" 462 | ], propertyKeywords = keySet(propertyKeywords_); 463 | 464 | var nonStandardPropertyKeywords_ = [ 465 | "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color", 466 | "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color", 467 | "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside", 468 | "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button", 469 | "searchfield-results-decoration", "zoom" 470 | ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_); 471 | 472 | var colorKeywords_ = [ 473 | "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", 474 | "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", 475 | "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", 476 | "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", 477 | "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", 478 | "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", 479 | "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", 480 | "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick", 481 | "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", 482 | "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew", 483 | "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", 484 | "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", 485 | "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink", 486 | "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", 487 | "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", 488 | "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", 489 | "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", 490 | "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", 491 | "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", 492 | "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred", 493 | "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", 494 | "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown", 495 | "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", 496 | "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", 497 | "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", 498 | "whitesmoke", "yellow", "yellowgreen" 499 | ], colorKeywords = keySet(colorKeywords_); 500 | 501 | var valueKeywords_ = [ 502 | "above", "absolute", "activeborder", "activecaption", "afar", 503 | "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", 504 | "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", 505 | "arabic-indic", "armenian", "asterisks", "auto", "avoid", "avoid-column", "avoid-page", 506 | "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary", 507 | "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", 508 | "both", "bottom", "break", "break-all", "break-word", "button", "button-bevel", 509 | "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", 510 | "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", 511 | "cell", "center", "checkbox", "circle", "cjk-earthly-branch", 512 | "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", 513 | "col-resize", "collapse", "column", "compact", "condensed", "contain", "content", 514 | "content-box", "context-menu", "continuous", "copy", "cover", "crop", 515 | "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", 516 | "decimal-leading-zero", "default", "default-button", "destination-atop", 517 | "destination-in", "destination-out", "destination-over", "devanagari", 518 | "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted", 519 | "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", 520 | "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", 521 | "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", 522 | "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", 523 | "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", 524 | "ethiopic-halehame-gez", "ethiopic-halehame-om-et", 525 | "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", 526 | "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", 527 | "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed", 528 | "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", 529 | "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove", 530 | "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", 531 | "help", "hidden", "hide", "higher", "highlight", "highlighttext", 532 | "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", 533 | "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", 534 | "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", 535 | "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", 536 | "italic", "justify", "kannada", "katakana", "katakana-iroha", "keep-all", "khmer", 537 | "landscape", "lao", "large", "larger", "left", "level", "lighter", 538 | "line-through", "linear", "lines", "list-item", "listbox", "listitem", 539 | "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", 540 | "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", 541 | "lower-roman", "lowercase", "ltr", "malayalam", "match", 542 | "media-controls-background", "media-current-time-display", 543 | "media-fullscreen-button", "media-mute-button", "media-play-button", 544 | "media-return-to-realtime-button", "media-rewind-button", 545 | "media-seek-back-button", "media-seek-forward-button", "media-slider", 546 | "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", 547 | "media-volume-slider-container", "media-volume-sliderthumb", "medium", 548 | "menu", "menulist", "menulist-button", "menulist-text", 549 | "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", 550 | "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize", 551 | "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", 552 | "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", 553 | "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", 554 | "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", 555 | "outside", "outside-shape", "overlay", "overline", "padding", "padding-box", 556 | "painted", "page", "paused", "persian", "plus-darker", "plus-lighter", "pointer", 557 | "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", 558 | "radio", "read-only", "read-write", "read-write-plaintext-only", "rectangle", "region", 559 | "relative", "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba", 560 | "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", 561 | "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield", 562 | "searchfield-cancel-button", "searchfield-decoration", 563 | "searchfield-results-button", "searchfield-results-decoration", 564 | "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", 565 | "single", "skip-white-space", "slide", "slider-horizontal", 566 | "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", 567 | "small", "small-caps", "small-caption", "smaller", "solid", "somali", 568 | "source-atop", "source-in", "source-out", "source-over", "space", "square", 569 | "square-button", "start", "static", "status-bar", "stretch", "stroke", 570 | "sub", "subpixel-antialiased", "super", "sw-resize", "table", 571 | "table-caption", "table-cell", "table-column", "table-column-group", 572 | "table-footer-group", "table-header-group", "table-row", "table-row-group", 573 | "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", 574 | "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", 575 | "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", 576 | "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", 577 | "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", 578 | "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", 579 | "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", 580 | "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", 581 | "visibleStroke", "visual", "w-resize", "wait", "wave", "wider", 582 | "window", "windowframe", "windowtext", "x-large", "x-small", "xor", 583 | "xx-large", "xx-small" 584 | ], valueKeywords = keySet(valueKeywords_); 585 | 586 | var fontProperties_ = [ 587 | "font-family", "src", "unicode-range", "font-variant", "font-feature-settings", 588 | "font-stretch", "font-weight", "font-style" 589 | ], fontProperties = keySet(fontProperties_); 590 | 591 | var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_) 592 | .concat(nonStandardPropertyKeywords_).concat(colorKeywords_).concat(valueKeywords_); 593 | CodeMirror.registerHelper("hintWords", "css", allWords); 594 | 595 | function tokenCComment(stream, state) { 596 | var maybeEnd = false, ch; 597 | while ((ch = stream.next()) != null) { 598 | if (maybeEnd && ch == "/") { 599 | state.tokenize = null; 600 | break; 601 | } 602 | maybeEnd = (ch == "*"); 603 | } 604 | return ["comment", "comment"]; 605 | } 606 | 607 | function tokenSGMLComment(stream, state) { 608 | if (stream.skipTo("-->")) { 609 | stream.match("-->"); 610 | state.tokenize = null; 611 | } else { 612 | stream.skipToEnd(); 613 | } 614 | return ["comment", "comment"]; 615 | } 616 | 617 | CodeMirror.defineMIME("text/css", { 618 | mediaTypes: mediaTypes, 619 | mediaFeatures: mediaFeatures, 620 | propertyKeywords: propertyKeywords, 621 | nonStandardPropertyKeywords: nonStandardPropertyKeywords, 622 | colorKeywords: colorKeywords, 623 | valueKeywords: valueKeywords, 624 | fontProperties: fontProperties, 625 | tokenHooks: { 626 | "<": function(stream, state) { 627 | if (!stream.match("!--")) return false; 628 | state.tokenize = tokenSGMLComment; 629 | return tokenSGMLComment(stream, state); 630 | }, 631 | "/": function(stream, state) { 632 | if (!stream.eat("*")) return false; 633 | state.tokenize = tokenCComment; 634 | return tokenCComment(stream, state); 635 | } 636 | }, 637 | name: "css" 638 | }); 639 | 640 | CodeMirror.defineMIME("text/x-scss", { 641 | mediaTypes: mediaTypes, 642 | mediaFeatures: mediaFeatures, 643 | propertyKeywords: propertyKeywords, 644 | nonStandardPropertyKeywords: nonStandardPropertyKeywords, 645 | colorKeywords: colorKeywords, 646 | valueKeywords: valueKeywords, 647 | fontProperties: fontProperties, 648 | allowNested: true, 649 | tokenHooks: { 650 | "/": function(stream, state) { 651 | if (stream.eat("/")) { 652 | stream.skipToEnd(); 653 | return ["comment", "comment"]; 654 | } else if (stream.eat("*")) { 655 | state.tokenize = tokenCComment; 656 | return tokenCComment(stream, state); 657 | } else { 658 | return ["operator", "operator"]; 659 | } 660 | }, 661 | ":": function(stream) { 662 | if (stream.match(/\s*\{/)) 663 | return [null, "{"]; 664 | return false; 665 | }, 666 | "$": function(stream) { 667 | stream.match(/^[\w-]+/); 668 | if (stream.match(/^\s*:/, false)) 669 | return ["variable-2", "variable-definition"]; 670 | return ["variable-2", "variable"]; 671 | }, 672 | "#": function(stream) { 673 | if (!stream.eat("{")) return false; 674 | return [null, "interpolation"]; 675 | } 676 | }, 677 | name: "css", 678 | helperType: "scss" 679 | }); 680 | 681 | CodeMirror.defineMIME("text/x-less", { 682 | mediaTypes: mediaTypes, 683 | mediaFeatures: mediaFeatures, 684 | propertyKeywords: propertyKeywords, 685 | nonStandardPropertyKeywords: nonStandardPropertyKeywords, 686 | colorKeywords: colorKeywords, 687 | valueKeywords: valueKeywords, 688 | fontProperties: fontProperties, 689 | allowNested: true, 690 | tokenHooks: { 691 | "/": function(stream, state) { 692 | if (stream.eat("/")) { 693 | stream.skipToEnd(); 694 | return ["comment", "comment"]; 695 | } else if (stream.eat("*")) { 696 | state.tokenize = tokenCComment; 697 | return tokenCComment(stream, state); 698 | } else { 699 | return ["operator", "operator"]; 700 | } 701 | }, 702 | "@": function(stream) { 703 | if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false; 704 | stream.eatWhile(/[\w\\\-]/); 705 | if (stream.match(/^\s*:/, false)) 706 | return ["variable-2", "variable-definition"]; 707 | return ["variable-2", "variable"]; 708 | }, 709 | "&": function() { 710 | return ["atom", "atom"]; 711 | } 712 | }, 713 | name: "css", 714 | helperType: "less" 715 | }); 716 | 717 | }); -------------------------------------------------------------------------------- /lib/filereader.js: -------------------------------------------------------------------------------- 1 | /*! 2 | FileReader.js - v0.99 3 | A lightweight wrapper for common FileReader usage. 4 | Copyright 2014 Brian Grinstead - MIT License. 5 | See http://github.com/bgrins/filereader.js for documentation. 6 | */ 7 | 8 | (function(window, document) { 9 | 10 | var FileReader = window.FileReader; 11 | var FileReaderSyncSupport = false; 12 | var workerScript = "self.addEventListener('message', function(e) { var data=e.data; try { var reader = new FileReaderSync; postMessage({ result: reader[data.readAs](data.file), extra: data.extra, file: data.file})} catch(e){ postMessage({ result:'error', extra:data.extra, file:data.file}); } }, false);"; 13 | var syncDetectionScript = "onmessage = function(e) { postMessage(!!FileReaderSync); };"; 14 | var fileReaderEvents = ['loadstart', 'progress', 'load', 'abort', 'error', 'loadend']; 15 | var sync = false; 16 | var FileReaderJS = window.FileReaderJS = { 17 | enabled: false, 18 | setupInput: setupInput, 19 | setupDrop: setupDrop, 20 | setupClipboard: setupClipboard, 21 | setSync: function (value) { 22 | sync = value; 23 | 24 | if (sync && !FileReaderSyncSupport) { 25 | checkFileReaderSyncSupport(); 26 | } 27 | }, 28 | getSync: function() { 29 | return sync && FileReaderSyncSupport; 30 | }, 31 | output: [], 32 | opts: { 33 | dragClass: "drag", 34 | accept: false, 35 | readAsDefault: 'DataURL', 36 | readAsMap: { 37 | }, 38 | on: { 39 | loadstart: noop, 40 | progress: noop, 41 | load: noop, 42 | abort: noop, 43 | error: noop, 44 | loadend: noop, 45 | skip: noop, 46 | groupstart: noop, 47 | groupend: noop, 48 | beforestart: noop 49 | } 50 | } 51 | }; 52 | 53 | // Setup jQuery plugin (if available) 54 | if (typeof(jQuery) !== "undefined") { 55 | jQuery.fn.fileReaderJS = function(opts) { 56 | return this.each(function() { 57 | if (jQuery(this).is("input")) { 58 | setupInput(this, opts); 59 | } 60 | else { 61 | setupDrop(this, opts); 62 | } 63 | }); 64 | }; 65 | 66 | jQuery.fn.fileClipboard = function(opts) { 67 | return this.each(function() { 68 | setupClipboard(this, opts); 69 | }); 70 | }; 71 | } 72 | 73 | // Not all browsers support the FileReader interface. Return with the enabled bit = false. 74 | if (!FileReader) { 75 | return; 76 | } 77 | 78 | 79 | // makeWorker is a little wrapper for generating web workers from strings 80 | function makeWorker(script) { 81 | var URL = window.URL || window.webkitURL; 82 | var Blob = window.Blob; 83 | var Worker = window.Worker; 84 | 85 | if (!URL || !Blob || !Worker || !script) { 86 | return null; 87 | } 88 | 89 | var blob = new Blob([script]); 90 | var worker = new Worker(URL.createObjectURL(blob)); 91 | return worker; 92 | } 93 | 94 | // setupClipboard: bind to clipboard events (intended for document.body) 95 | function setupClipboard(element, opts) { 96 | 97 | if (!FileReaderJS.enabled) { 98 | return; 99 | } 100 | var instanceOptions = extend(extend({}, FileReaderJS.opts), opts); 101 | 102 | element.addEventListener("paste", onpaste, false); 103 | 104 | function onpaste(e) { 105 | var files = []; 106 | var clipboardData = e.clipboardData || {}; 107 | var items = clipboardData.items || []; 108 | 109 | for (var i = 0; i < items.length; i++) { 110 | var file = items[i].getAsFile(); 111 | 112 | if (file) { 113 | 114 | // Create a fake file name for images from clipboard, since this data doesn't get sent 115 | var matches = new RegExp("/\(.*\)").exec(file.type); 116 | if (!file.name && matches) { 117 | var extension = matches[1]; 118 | file.name = "clipboard" + i + "." + extension; 119 | } 120 | 121 | files.push(file); 122 | } 123 | } 124 | 125 | if (files.length) { 126 | processFileList(e, files, instanceOptions); 127 | e.preventDefault(); 128 | e.stopPropagation(); 129 | } 130 | } 131 | } 132 | 133 | // setupInput: bind the 'change' event to an input[type=file] 134 | function setupInput(input, opts) { 135 | 136 | if (!FileReaderJS.enabled) { 137 | return; 138 | } 139 | var instanceOptions = extend(extend({}, FileReaderJS.opts), opts); 140 | 141 | input.addEventListener("change", inputChange, false); 142 | input.addEventListener("drop", inputDrop, false); 143 | 144 | function inputChange(e) { 145 | processFileList(e, input.files, instanceOptions); 146 | } 147 | 148 | function inputDrop(e) { 149 | e.stopPropagation(); 150 | e.preventDefault(); 151 | processFileList(e, e.dataTransfer.files, instanceOptions); 152 | } 153 | } 154 | 155 | // setupDrop: bind the 'drop' event for a DOM element 156 | function setupDrop(dropbox, opts) { 157 | 158 | if (!FileReaderJS.enabled) { 159 | return; 160 | } 161 | var instanceOptions = extend(extend({}, FileReaderJS.opts), opts); 162 | var dragClass = instanceOptions.dragClass; 163 | var initializedOnBody = false; 164 | 165 | // Bind drag events to the dropbox to add the class while dragging, and accept the drop data transfer. 166 | dropbox.addEventListener("dragenter", onlyWithFiles(dragenter), false); 167 | dropbox.addEventListener("dragleave", onlyWithFiles(dragleave), false); 168 | dropbox.addEventListener("dragover", onlyWithFiles(dragover), false); 169 | dropbox.addEventListener("drop", onlyWithFiles(drop), false); 170 | 171 | // Bind to body to prevent the dropbox events from firing when it was initialized on the page. 172 | document.body.addEventListener("dragstart", bodydragstart, true); 173 | document.body.addEventListener("dragend", bodydragend, true); 174 | document.body.addEventListener("drop", bodydrop, false); 175 | 176 | function bodydragend(e) { 177 | initializedOnBody = false; 178 | } 179 | 180 | function bodydragstart(e) { 181 | initializedOnBody = true; 182 | } 183 | 184 | function bodydrop(e) { 185 | if (e.dataTransfer.files && e.dataTransfer.files.length ){ 186 | e.stopPropagation(); 187 | e.preventDefault(); 188 | } 189 | } 190 | 191 | function onlyWithFiles(fn) { 192 | return function() { 193 | if (!initializedOnBody) { 194 | fn.apply(this, arguments); 195 | } 196 | }; 197 | } 198 | 199 | function drop(e) { 200 | e.stopPropagation(); 201 | e.preventDefault(); 202 | if (dragClass) { 203 | removeClass(dropbox, dragClass); 204 | } 205 | processFileList(e, e.dataTransfer.files, instanceOptions); 206 | } 207 | 208 | function dragenter(e) { 209 | e.stopPropagation(); 210 | e.preventDefault(); 211 | if (dragClass) { 212 | addClass(dropbox, dragClass); 213 | } 214 | } 215 | 216 | function dragleave(e) { 217 | if (dragClass) { 218 | removeClass(dropbox, dragClass); 219 | } 220 | } 221 | 222 | function dragover(e) { 223 | e.stopPropagation(); 224 | e.preventDefault(); 225 | if (dragClass) { 226 | addClass(dropbox, dragClass); 227 | } 228 | } 229 | } 230 | 231 | // setupCustomFileProperties: modify the file object with extra properties 232 | function setupCustomFileProperties(files, groupID) { 233 | for (var i = 0; i < files.length; i++) { 234 | var file = files[i]; 235 | file.extra = { 236 | nameNoExtension: file.name.substring(0, file.name.lastIndexOf('.')), 237 | extension: file.name.substring(file.name.lastIndexOf('.') + 1), 238 | fileID: i, 239 | uniqueID: getUniqueID(), 240 | groupID: groupID, 241 | prettySize: prettySize(file.size) 242 | }; 243 | } 244 | } 245 | 246 | // getReadAsMethod: return method name for 'readAs*' - http://www.w3.org/TR/FileAPI/#reading-a-file 247 | function getReadAsMethod(type, readAsMap, readAsDefault) { 248 | for (var r in readAsMap) { 249 | if (type.match(new RegExp(r))) { 250 | return 'readAs' + readAsMap[r]; 251 | } 252 | } 253 | 254 | return 'readAs' + readAsDefault; 255 | } 256 | 257 | // processFileList: read the files with FileReader, send off custom events. 258 | function processFileList(e, files, opts) { 259 | 260 | var filesLeft = files.length; 261 | var group = { 262 | groupID: getGroupID(), 263 | files: files, 264 | started: new Date() 265 | }; 266 | 267 | function groupEnd() { 268 | group.ended = new Date(); 269 | opts.on.groupend(group); 270 | } 271 | 272 | function groupFileDone() { 273 | if (--filesLeft === 0) { 274 | groupEnd(); 275 | } 276 | } 277 | 278 | FileReaderJS.output.push(group); 279 | setupCustomFileProperties(files, group.groupID); 280 | 281 | opts.on.groupstart(group); 282 | 283 | // No files in group - end immediately 284 | if (!files.length) { 285 | groupEnd(); 286 | return; 287 | } 288 | 289 | var supportsSync = sync && FileReaderSyncSupport; 290 | var syncWorker; 291 | 292 | // Only initialize the synchronous worker if the option is enabled - to prevent the overhead 293 | if (supportsSync) { 294 | syncWorker = makeWorker(workerScript); 295 | syncWorker.onmessage = function(e) { 296 | var file = e.data.file; 297 | var result = e.data.result; 298 | 299 | // Workers seem to lose the custom property on the file object. 300 | if (!file.extra) { 301 | file.extra = e.data.extra; 302 | } 303 | 304 | file.extra.ended = new Date(); 305 | 306 | // Call error or load event depending on success of the read from the worker. 307 | opts.on[result === "error" ? "error" : "load"]({ target: { result: result } }, file); 308 | groupFileDone(); 309 | }; 310 | } 311 | 312 | Array.prototype.forEach.call(files, function(file) { 313 | 314 | file.extra.started = new Date(); 315 | 316 | if (opts.accept && !file.type.match(new RegExp(opts.accept))) { 317 | opts.on.skip(file); 318 | groupFileDone(); 319 | return; 320 | } 321 | 322 | if (opts.on.beforestart(file) === false) { 323 | opts.on.skip(file); 324 | groupFileDone(); 325 | return; 326 | } 327 | 328 | var readAs = getReadAsMethod(file.type, opts.readAsMap, opts.readAsDefault); 329 | 330 | if (syncWorker) { 331 | syncWorker.postMessage({ 332 | file: file, 333 | extra: file.extra, 334 | readAs: readAs 335 | }); 336 | } 337 | else { 338 | 339 | var reader = new FileReader(); 340 | reader.originalEvent = e; 341 | 342 | fileReaderEvents.forEach(function(eventName) { 343 | reader['on' + eventName] = function(e) { 344 | if (eventName == 'load' || eventName == 'error') { 345 | file.extra.ended = new Date(); 346 | } 347 | opts.on[eventName](e, file); 348 | if (eventName == 'loadend') { 349 | groupFileDone(); 350 | } 351 | }; 352 | }); 353 | reader[readAs](file); 354 | } 355 | }); 356 | } 357 | 358 | // checkFileReaderSyncSupport: Create a temporary worker and see if FileReaderSync exists 359 | function checkFileReaderSyncSupport() { 360 | var worker = makeWorker(syncDetectionScript); 361 | if (worker) { 362 | worker.onmessage =function(e) { 363 | FileReaderSyncSupport = e.data; 364 | }; 365 | worker.postMessage({}); 366 | } 367 | } 368 | 369 | // noop: do nothing 370 | function noop() { 371 | 372 | } 373 | 374 | // extend: used to make deep copies of options object 375 | function extend(destination, source) { 376 | for (var property in source) { 377 | if (source[property] && source[property].constructor && 378 | source[property].constructor === Object) { 379 | destination[property] = destination[property] || {}; 380 | arguments.callee(destination[property], source[property]); 381 | } 382 | else { 383 | destination[property] = source[property]; 384 | } 385 | } 386 | return destination; 387 | } 388 | 389 | // hasClass: does an element have the css class? 390 | function hasClass(el, name) { 391 | return new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)").test(el.className); 392 | } 393 | 394 | // addClass: add the css class for the element. 395 | function addClass(el, name) { 396 | if (!hasClass(el, name)) { 397 | el.className = el.className ? [el.className, name].join(' ') : name; 398 | } 399 | } 400 | 401 | // removeClass: remove the css class from the element. 402 | function removeClass(el, name) { 403 | if (hasClass(el, name)) { 404 | var c = el.className; 405 | el.className = c.replace(new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)", "g"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 406 | } 407 | } 408 | 409 | // prettySize: convert bytes to a more readable string. 410 | function prettySize(bytes) { 411 | var s = ['bytes', 'kb', 'MB', 'GB', 'TB', 'PB']; 412 | var e = Math.floor(Math.log(bytes)/Math.log(1024)); 413 | return (bytes/Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e]; 414 | } 415 | 416 | // getGroupID: generate a unique int ID for groups. 417 | var getGroupID = (function(id) { 418 | return function() { 419 | return id++; 420 | }; 421 | })(0); 422 | 423 | // getUniqueID: generate a unique int ID for files 424 | var getUniqueID = (function(id) { 425 | return function() { 426 | return id++; 427 | }; 428 | })(0); 429 | 430 | // The interface is supported, bind the FileReaderJS callbacks 431 | FileReaderJS.enabled = true; 432 | 433 | })(this, document); -------------------------------------------------------------------------------- /lib/inflection.js: -------------------------------------------------------------------------------- 1 | var InflectionJS = (function() { 2 | 3 | /* 4 | This is a list of nouns that use the same form for both singular and plural. 5 | This list should remain entirely in lower case to correctly match Strings. 6 | */ 7 | var uncountable_words = [ 8 | 'equipment', 'information', 'rice', 'money', 'species', 'series', 9 | 'fish', 'sheep', 'moose', 'deer', 'news' 10 | ]; 11 | 12 | /* 13 | These rules translate from the singular form of a noun to its plural form. 14 | */ 15 | var plural_rules = [ 16 | [new RegExp('(m)an$', 'gi'), '$1en'], 17 | [new RegExp('(pe)rson$', 'gi'), '$1ople'], 18 | [new RegExp('(child)$', 'gi'), '$1ren'], 19 | [new RegExp('^(ox)$', 'gi'), '$1en'], 20 | [new RegExp('(ax|test)is$', 'gi'), '$1es'], 21 | [new RegExp('(octop|vir)us$', 'gi'), '$1i'], 22 | [new RegExp('(alias|status)$', 'gi'), '$1es'], 23 | [new RegExp('(bu)s$', 'gi'), '$1ses'], 24 | [new RegExp('(buffal|tomat|potat)o$', 'gi'), '$1oes'], 25 | [new RegExp('([ti])um$', 'gi'), '$1a'], 26 | [new RegExp('sis$', 'gi'), 'ses'], 27 | [new RegExp('(?:([^f])fe|([lr])f)$', 'gi'), '$1$2ves'], 28 | [new RegExp('(hive)$', 'gi'), '$1s'], 29 | [new RegExp('([^aeiouy]|qu)y$', 'gi'), '$1ies'], 30 | [new RegExp('(x|ch|ss|sh)$', 'gi'), '$1es'], 31 | [new RegExp('(matr|vert|ind)ix|ex$', 'gi'), '$1ices'], 32 | [new RegExp('([m|l])ouse$', 'gi'), '$1ice'], 33 | [new RegExp('(quiz)$', 'gi'), '$1zes'], 34 | [new RegExp('s$', 'gi'), 's'], 35 | [new RegExp('$', 'gi'), 's'] 36 | ]; 37 | 38 | /* 39 | These rules translate from the plural form of a noun to its singular form. 40 | */ 41 | var singular_rules = [ 42 | [new RegExp('(m)en$', 'gi'), '$1an'], 43 | [new RegExp('(pe)ople$', 'gi'), '$1rson'], 44 | [new RegExp('(child)ren$', 'gi'), '$1'], 45 | [new RegExp('([ti])a$', 'gi'), '$1um'], 46 | [new RegExp('((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$', 'gi'), '$1$2sis'], 47 | [new RegExp('(hive)s$', 'gi'), '$1'], 48 | [new RegExp('(tive)s$', 'gi'), '$1'], 49 | [new RegExp('(curve)s$', 'gi'), '$1'], 50 | [new RegExp('([lr])ves$', 'gi'), '$1f'], 51 | [new RegExp('([^fo])ves$', 'gi'), '$1fe'], 52 | [new RegExp('([^aeiouy]|qu)ies$', 'gi'), '$1y'], 53 | [new RegExp('(s)eries$', 'gi'), '$1eries'], 54 | [new RegExp('(m)ovies$', 'gi'), '$1ovie'], 55 | [new RegExp('(x|ch|ss|sh)es$', 'gi'), '$1'], 56 | [new RegExp('([m|l])ice$', 'gi'), '$1ouse'], 57 | [new RegExp('(bus)es$', 'gi'), '$1'], 58 | [new RegExp('(o)es$', 'gi'), '$1'], 59 | [new RegExp('(shoe)s$', 'gi'), '$1'], 60 | [new RegExp('(cris|ax|test)es$', 'gi'), '$1is'], 61 | [new RegExp('(octop|vir)i$', 'gi'), '$1us'], 62 | [new RegExp('(alias|status)es$', 'gi'), '$1'], 63 | [new RegExp('^(ox)en', 'gi'), '$1'], 64 | [new RegExp('(vert|ind)ices$', 'gi'), '$1ex'], 65 | [new RegExp('(matr)ices$', 'gi'), '$1ix'], 66 | [new RegExp('(quiz)zes$', 'gi'), '$1'], 67 | [new RegExp('s$', 'gi'), ''] 68 | ]; 69 | 70 | /* 71 | This is a list of words that should not be capitalized for title case 72 | */ 73 | var non_titlecased_words = [ 74 | 'and', 'or', 'nor', 'a', 'an', 'the', 'so', 'but', 'to', 'of', 'at', 75 | 'by', 'from', 'into', 'on', 'onto', 'off', 'out', 'in', 'over', 76 | 'with', 'for' 77 | ]; 78 | 79 | /* 80 | These are regular expressions used for converting between String formats 81 | */ 82 | var id_suffix = new RegExp('(_ids|_id)$', 'g'); 83 | var underbar = new RegExp('_', 'g'); 84 | var space_or_underbar = new RegExp('[\ _]', 'g'); 85 | var uppercase = new RegExp('([A-Z])', 'g'); 86 | var underbar_prefix = new RegExp('^_'); 87 | 88 | /* 89 | This is a helper method that applies rules based replacement to a String 90 | Signature: 91 | apply_rules(str, rules, skip, override) == String 92 | Arguments: 93 | str - String - String to modify and return based on the passed rules 94 | rules - Array: [RegExp, String] - Regexp to match paired with String to use for replacement 95 | skip - Array: [String] - Strings to skip if they match 96 | override - String (optional) - String to return as though this method succeeded (used to conform to APIs) 97 | Returns: 98 | String - passed String modified by passed rules 99 | Examples: 100 | apply_rules("cows", singular_rules) === 'cow' 101 | */ 102 | var apply_rules = function(str, rules, skip, override) { 103 | if (override) { 104 | str = override; 105 | } else { 106 | var ignore = (skip.indexOf(str.toLowerCase()) > -1); 107 | if (!ignore) { 108 | for (var x = 0; x < rules.length; x++) { 109 | if (str.match(rules[x][0])) { 110 | str = str.replace(rules[x][0], rules[x][1]); 111 | break; 112 | } 113 | } 114 | } 115 | } 116 | return str; 117 | }; 118 | 119 | 120 | /* 121 | This lets us detect if an Array contains a given element in IE < 9 122 | Signature: 123 | Array.indexOf(item, fromIndex, compareFunc) == Integer 124 | Arguments: 125 | item - Object - object to locate in the Array 126 | fromIndex - Integer (optional) - starts checking from this position in the Array 127 | compareFunc - Function (optional) - function used to compare Array item vs passed item 128 | Returns: 129 | Integer - index position in the Array of the passed item 130 | Examples: 131 | ['hi','there'].indexOf("guys") === -1 132 | ['hi','there'].indexOf("hi") === 0 133 | */ 134 | if (!Array.prototype.indexOf) { 135 | Array.prototype.indexOf = function(item, fromIndex, compareFunc) { 136 | if (!fromIndex) { 137 | fromIndex = -1; 138 | } 139 | var index = -1; 140 | for (var i = fromIndex; i < this.length; i++) { 141 | if (this[i] === item || compareFunc && compareFunc(this[i], item)) { 142 | index = i; 143 | break; 144 | } 145 | } 146 | return index; 147 | }; 148 | } 149 | 150 | /* 151 | This function adds plurilization support to every String object 152 | Signature: 153 | String.pluralize(plural) == String 154 | Arguments: 155 | plural - String (optional) - overrides normal output with said String 156 | Returns: 157 | String - singular English language nouns are returned in plural form 158 | Examples: 159 | "person".pluralize() == "people" 160 | "octopus".pluralize() == "octopi" 161 | "Hat".pluralize() == "Hats" 162 | "person".pluralize("guys") == "guys" 163 | */ 164 | var pluralize = function(string, plural) { 165 | return apply_rules(string, plural_rules, uncountable_words, plural); 166 | }; 167 | 168 | /* 169 | This function adds singularization support to every String object 170 | Signature: 171 | String.singularize(singular) == String 172 | Arguments: 173 | singular - String (optional) - overrides normal output with said String 174 | Returns: 175 | String - plural English language nouns are returned in singular form 176 | Examples: 177 | "people".singularize() == "person" 178 | "octopi".singularize() == "octopus" 179 | "Hats".singularize() == "Hat" 180 | "guys".singularize("person") == "person" 181 | */ 182 | var singularize = function(string, singular) { 183 | return apply_rules(string, singular_rules, uncountable_words, singular); 184 | }; 185 | 186 | /* 187 | This function adds camelization support to every String object 188 | Signature: 189 | String.camelize(lowFirstLetter) == String 190 | Arguments: 191 | lowFirstLetter - boolean (optional) - default is to capitalize the first 192 | letter of the results... passing true will lowercase it 193 | Returns: 194 | String - lower case underscored words will be returned in camel case 195 | additionally '/' is translated to '::' 196 | Examples: 197 | "message_properties".camelize() == "MessageProperties" 198 | "message_properties".camelize(true) == "messageProperties" 199 | */ 200 | var camelize = function(string, lowFirstLetter) { 201 | string = string.toLowerCase(); 202 | var str_path = string.split('/'); 203 | for (var i = 0; i < str_path.length; i++) { 204 | var str_arr = str_path[i].split('_'); 205 | var initX = ((lowFirstLetter && i + 1 === str_path.length) ? (1) : (0)); 206 | for (var x = initX; x < str_arr.length; x++) { 207 | str_arr[x] = str_arr[x].charAt(0).toUpperCase() + str_arr[x].substring(1); 208 | } 209 | str_path[i] = str_arr.join(''); 210 | } 211 | string = str_path.join('::'); 212 | return string; 213 | }; 214 | 215 | /* 216 | This function adds underscore support to every String object 217 | Signature: 218 | String.underscore() == String 219 | Arguments: 220 | N/A 221 | Returns: 222 | String - camel cased words are returned as lower cased and underscored 223 | additionally '::' is translated to '/' 224 | Examples: 225 | "MessageProperties".camelize() == "message_properties" 226 | "messageProperties".underscore() == "message_properties" 227 | */ 228 | var underscore = function(string) { 229 | var str_path = string.split('::'); 230 | for (var i = 0; i < str_path.length; i++) { 231 | str_path[i] = str_path[i].replace(uppercase, '_$1'); 232 | str_path[i] = str_path[i].replace(underbar_prefix, ''); 233 | } 234 | string = str_path.join('/').toLowerCase(); 235 | return string; 236 | }; 237 | 238 | /* 239 | This function adds humanize support to every String object 240 | Signature: 241 | String.humanize(lowFirstLetter) == String 242 | Arguments: 243 | lowFirstLetter - boolean (optional) - default is to capitalize the first 244 | letter of the results... passing true will lowercase it 245 | Returns: 246 | String - lower case underscored words will be returned in humanized form 247 | Examples: 248 | "message_properties".humanize() == "Message properties" 249 | "message_properties".humanize(true) == "message properties" 250 | */ 251 | var humanize = function(string, lowFirstLetter) { 252 | string = string.toLowerCase(); 253 | string = string.replace(id_suffix, ''); 254 | string = string.replace(underbar, ' '); 255 | if (!lowFirstLetter) { 256 | string = capitalize(string); 257 | } 258 | return string; 259 | }; 260 | 261 | /* 262 | This function adds capitalization support to every String object 263 | Signature: 264 | String.capitalize() == String 265 | Arguments: 266 | N/A 267 | Returns: 268 | String - all characters will be lower case and the first will be upper 269 | Examples: 270 | "message_properties".capitalize() == "Message_properties" 271 | "message properties".capitalize() == "Message properties" 272 | */ 273 | var capitalize = function(string) { 274 | string = string.toLowerCase(); 275 | string = string.substring(0, 1).toUpperCase() + string.substring(1); 276 | return string; 277 | }; 278 | 279 | /* 280 | This function adds dasherization support to every String object 281 | Signature: 282 | String.dasherize() == String 283 | Arguments: 284 | N/A 285 | Returns: 286 | String - replaces all spaces or underbars with dashes 287 | Examples: 288 | "message_properties".capitalize() == "message-properties" 289 | "Message Properties".capitalize() == "Message-Properties" 290 | */ 291 | var dasherize = function(string) { 292 | string = string.replace(space_or_underbar, '-'); 293 | return string; 294 | }; 295 | 296 | /* 297 | This function adds titleize support to every String object 298 | Signature: 299 | String.titleize() == String 300 | Arguments: 301 | N/A 302 | Returns: 303 | String - capitalizes words as you would for a book title 304 | Examples: 305 | "message_properties".titleize() == "Message Properties" 306 | "message properties to keep".titleize() == "Message Properties to Keep" 307 | */ 308 | var titleize = function(string) { 309 | string = string.toLowerCase(); 310 | string = string.replace(underbar, ' '); 311 | var str_arr = string.split(' '); 312 | for (var x = 0; x < str_arr.length; x++) { 313 | var d = str_arr[x].split('-'); 314 | for (var i = 0; i < d.length; i++) { 315 | if (non_titlecased_words.indexOf(d[i].toLowerCase()) < 0) { 316 | d[i] = capitalize(d[i]); 317 | } 318 | } 319 | str_arr[x] = d.join('-'); 320 | } 321 | string = str_arr.join(' '); 322 | string = string.substring(0, 1).toUpperCase() + string.substring(1); 323 | return string; 324 | }; 325 | 326 | /* 327 | This function adds demodulize support to every String object 328 | Signature: 329 | String.demodulize() == String 330 | Arguments: 331 | N/A 332 | Returns: 333 | String - removes module names leaving only class names (Ruby style) 334 | Examples: 335 | "Message::Bus::Properties".demodulize() == "Properties" 336 | */ 337 | var demodulize = function(string) { 338 | var str_arr = string.split('::'); 339 | string = str_arr[str_arr.length - 1]; 340 | return string; 341 | }; 342 | 343 | /* 344 | This function adds tableize support to every String object 345 | Signature: 346 | String.tableize() == String 347 | Arguments: 348 | N/A 349 | Returns: 350 | String - renders camel cased words into their underscored plural form 351 | Examples: 352 | "MessageBusProperty".tableize() == "message_bus_properties" 353 | */ 354 | var tableize = function(string) { 355 | string = underscore(string); 356 | string = pluralize(string); 357 | return string; 358 | }; 359 | 360 | /* 361 | This function adds classification support to every String object 362 | Signature: 363 | String.classify() == String 364 | Arguments: 365 | N/A 366 | Returns: 367 | String - underscored plural nouns become the camel cased singular form 368 | Examples: 369 | "message_bus_properties".classify() == "MessageBusProperty" 370 | */ 371 | var classify = function(string) { 372 | string = camelize(string); 373 | string = singularize(string); 374 | return string; 375 | }; 376 | 377 | /* 378 | This function adds foreign key support to every String object 379 | Signature: 380 | String.foreign_key(dropIdUbar) == String 381 | Arguments: 382 | dropIdUbar - boolean (optional) - default is to seperate id with an 383 | underbar at the end of the class name, you can pass true to skip it 384 | Returns: 385 | String - camel cased singular class names become underscored with id 386 | Examples: 387 | "MessageBusProperty".foreign_key() == "message_bus_property_id" 388 | "MessageBusProperty".foreign_key(true) == "message_bus_propertyid" 389 | */ 390 | var foreign_key = function(string, dropIdUbar) { 391 | string = demodulize(string); 392 | string = underscore(string) + ((dropIdUbar) ? ('') : ('_')) + 'id'; 393 | return string; 394 | }; 395 | 396 | /* 397 | This function adds ordinalize support to every String object 398 | Signature: 399 | String.ordinalize() == String 400 | Arguments: 401 | N/A 402 | Returns: 403 | String - renders all found numbers their sequence like "22nd" 404 | Examples: 405 | "the 1 pitch".ordinalize() == "the 1st pitch" 406 | */ 407 | var ordinalize = function(string) { 408 | var str_arr = string.split(' '); 409 | for (var x = 0; x < str_arr.length; x++) { 410 | var i = parseInt(str_arr[x]); 411 | if (!isNaN(i)) { 412 | var ltd = str_arr[x].substring(str_arr[x].length - 2); 413 | var ld = str_arr[x].substring(str_arr[x].length - 1); 414 | var suf = "th"; 415 | if (ltd != "11" && ltd != "12" && ltd != "13") { 416 | if (ld === "1") { 417 | suf = "st"; 418 | } 419 | else if (ld === "2") { 420 | suf = "nd"; 421 | } 422 | else if (ld === "3") { 423 | suf = "rd"; 424 | } 425 | } 426 | str_arr[x] += suf; 427 | } 428 | } 429 | string = str_arr.join(' '); 430 | return string; 431 | }; 432 | 433 | return { 434 | pluralize: pluralize, 435 | singularize: singularize, 436 | camelize: camelize, 437 | underscore: underscore, 438 | humanize: humanize, 439 | capitalize: capitalize, 440 | dasherize: dasherize, 441 | titleize: titleize, 442 | demodulize: demodulize, 443 | tableize: tableize, 444 | classify: classify, 445 | foreign_key: foreign_key, 446 | ordinalize: ordinalize 447 | }; 448 | 449 | })(); -------------------------------------------------------------------------------- /lib/jsbeautify/beautify-css.js: -------------------------------------------------------------------------------- 1 | /*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ 2 | /* 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2007-2013 Einar Lielmanis and contributors. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation files 10 | (the "Software"), to deal in the Software without restriction, 11 | including without limitation the rights to use, copy, modify, merge, 12 | publish, distribute, sublicense, and/or sell copies of the Software, 13 | and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | CSS Beautifier 30 | --------------- 31 | 32 | Written by Harutyun Amirjanyan, (amirjanyan@gmail.com) 33 | 34 | Based on code initially developed by: Einar Lielmanis, 35 | http://jsbeautifier.org/ 36 | 37 | Usage: 38 | css_beautify(source_text); 39 | css_beautify(source_text, options); 40 | 41 | The options are (default in brackets): 42 | indent_size (4) — indentation size, 43 | indent_char (space) — character to indent with, 44 | selector_separator_newline (true) - separate selectors with newline or 45 | not (e.g. "a,\nbr" or "a, br") 46 | end_with_newline (false) - end with a newline 47 | 48 | e.g 49 | 50 | css_beautify(css_source_text, { 51 | 'indent_size': 1, 52 | 'indent_char': '\t', 53 | 'selector_separator': ' ', 54 | 'end_with_newline': false, 55 | }); 56 | */ 57 | 58 | // http://www.w3.org/TR/CSS21/syndata.html#tokenization 59 | // http://www.w3.org/TR/css3-syntax/ 60 | 61 | (function() { 62 | function css_beautify(source_text, options) { 63 | options = options || {}; 64 | var indentSize = options.indent_size || 4; 65 | var indentCharacter = options.indent_char || ' '; 66 | var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline; 67 | var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; 68 | 69 | // compatibility 70 | if (typeof indentSize === "string") { 71 | indentSize = parseInt(indentSize, 10); 72 | } 73 | 74 | 75 | // tokenizer 76 | var whiteRe = /^\s+$/; 77 | var wordRe = /[\w$\-_]/; 78 | 79 | var pos = -1, 80 | ch; 81 | 82 | function next() { 83 | ch = source_text.charAt(++pos); 84 | return ch || ''; 85 | } 86 | 87 | function peek(skipWhitespace) { 88 | var prev_pos = pos; 89 | if (skipWhitespace) { 90 | eatWhitespace(); 91 | } 92 | result = source_text.charAt(pos + 1) || ''; 93 | pos = prev_pos - 1; 94 | next(); 95 | return result; 96 | } 97 | 98 | function eatString(endChars) { 99 | var start = pos; 100 | while (next()) { 101 | if (ch === "\\") { 102 | next(); 103 | } else if (endChars.indexOf(ch) !== -1) { 104 | break; 105 | } else if (ch === "\n") { 106 | break; 107 | } 108 | } 109 | return source_text.substring(start, pos + 1); 110 | } 111 | 112 | function peekString(endChar) { 113 | var prev_pos = pos; 114 | var str = eatString(endChar); 115 | pos = prev_pos - 1; 116 | next(); 117 | return str; 118 | } 119 | 120 | function eatWhitespace() { 121 | var result = ''; 122 | while (whiteRe.test(peek())) { 123 | next() 124 | result += ch; 125 | } 126 | return result; 127 | } 128 | 129 | function skipWhitespace() { 130 | var result = ''; 131 | if (ch && whiteRe.test(ch)) { 132 | result = ch; 133 | } 134 | while (whiteRe.test(next())) { 135 | result += ch 136 | } 137 | return result; 138 | } 139 | 140 | function eatComment(singleLine) { 141 | var start = pos; 142 | var singleLine = peek() === "/"; 143 | next(); 144 | while (next()) { 145 | if (!singleLine && ch === "*" && peek() === "/") { 146 | next(); 147 | break; 148 | } else if (singleLine && ch === "\n") { 149 | return source_text.substring(start, pos); 150 | } 151 | } 152 | 153 | return source_text.substring(start, pos) + ch; 154 | } 155 | 156 | 157 | function lookBack(str) { 158 | return source_text.substring(pos - str.length, pos).toLowerCase() === 159 | str; 160 | } 161 | 162 | // Nested pseudo-class if we are insideRule 163 | // and the next special character found opens 164 | // a new block 165 | function foundNestedPseudoClass() { 166 | for (var i = pos + 1; i < source_text.length; i++){ 167 | var ch = source_text.charAt(i); 168 | if (ch === "{"){ 169 | return true; 170 | } else if (ch === ";" || ch === "}" || ch === ")") { 171 | return false; 172 | } 173 | } 174 | return false; 175 | } 176 | 177 | // printer 178 | var basebaseIndentString = source_text.match(/^[\t ]*/)[0]; 179 | var singleIndent = new Array(indentSize + 1).join(indentCharacter); 180 | var indentLevel = 0; 181 | var nestedLevel = 0; 182 | 183 | function indent() { 184 | indentLevel++; 185 | basebaseIndentString += singleIndent; 186 | } 187 | 188 | function outdent() { 189 | indentLevel--; 190 | basebaseIndentString = basebaseIndentString.slice(0, -indentSize); 191 | } 192 | 193 | var print = {}; 194 | print["{"] = function(ch) { 195 | print.singleSpace(); 196 | output.push(ch); 197 | print.newLine(); 198 | }; 199 | print["}"] = function(ch) { 200 | print.newLine(); 201 | output.push(ch); 202 | print.newLine(); 203 | }; 204 | 205 | print._lastCharWhitespace = function() { 206 | return whiteRe.test(output[output.length - 1]); 207 | }; 208 | 209 | print.newLine = function(keepWhitespace) { 210 | if (!keepWhitespace) { 211 | print.trim(); 212 | } 213 | 214 | if (output.length) { 215 | output.push('\n'); 216 | } 217 | if (basebaseIndentString) { 218 | output.push(basebaseIndentString); 219 | } 220 | }; 221 | print.singleSpace = function() { 222 | if (output.length && !print._lastCharWhitespace()) { 223 | output.push(' '); 224 | } 225 | }; 226 | 227 | print.trim = function() { 228 | while (print._lastCharWhitespace()) { 229 | output.pop(); 230 | } 231 | }; 232 | 233 | 234 | var output = []; 235 | if (basebaseIndentString) { 236 | output.push(basebaseIndentString); 237 | } 238 | /*_____________________--------------------_____________________*/ 239 | 240 | var insideRule = false; 241 | var enteringConditionalGroup = false; 242 | var top_ch = ''; 243 | var last_top_ch = ''; 244 | 245 | while (true) { 246 | var whitespace = skipWhitespace(); 247 | var isAfterSpace = whitespace !== ''; 248 | var isAfterNewline = whitespace.indexOf('\n') !== -1; 249 | var last_top_ch = top_ch; 250 | var top_ch = ch; 251 | 252 | if (!ch) { 253 | break; 254 | } else if (ch === '/' && peek() === '*') { /* css comment */ 255 | var header = lookBack(""); 256 | print.newLine(); 257 | output.push(eatComment()); 258 | print.newLine(); 259 | if (header) { 260 | print.newLine(true); 261 | } 262 | } else if (ch === '/' && peek() === '/') { // single line comment 263 | if (!isAfterNewline && last_top_ch !== '{') { 264 | print.trim(); 265 | } 266 | print.singleSpace(); 267 | output.push(eatComment()); 268 | print.newLine(); 269 | } else if (ch === '@') { 270 | // pass along the space we found as a separate item 271 | if (isAfterSpace) { 272 | print.singleSpace(); 273 | } 274 | output.push(ch); 275 | 276 | // strip trailing space, if present, for hash property checks 277 | var variableOrRule = peekString(": ,;{}()[]/='\"").replace(/\s$/, ''); 278 | 279 | // might be a nesting at-rule 280 | if (variableOrRule in css_beautify.NESTED_AT_RULE) { 281 | nestedLevel += 1; 282 | if (variableOrRule in css_beautify.CONDITIONAL_GROUP_RULE) { 283 | enteringConditionalGroup = true; 284 | } 285 | } else if (': '.indexOf(variableOrRule[variableOrRule.length -1]) >= 0) { 286 | //we have a variable, add it and insert one space before continuing 287 | next(); 288 | variableOrRule = eatString(": ").replace(/\s$/, ''); 289 | output.push(variableOrRule); 290 | print.singleSpace(); 291 | } 292 | } else if (ch === '{') { 293 | if (peek(true) === '}') { 294 | eatWhitespace(); 295 | next(); 296 | print.singleSpace(); 297 | output.push("{}"); 298 | } else { 299 | indent(); 300 | print["{"](ch); 301 | // when entering conditional groups, only rulesets are allowed 302 | if (enteringConditionalGroup) { 303 | enteringConditionalGroup = false; 304 | insideRule = (indentLevel > nestedLevel); 305 | } else { 306 | // otherwise, declarations are also allowed 307 | insideRule = (indentLevel >= nestedLevel); 308 | } 309 | } 310 | } else if (ch === '}') { 311 | outdent(); 312 | print["}"](ch); 313 | insideRule = false; 314 | if (nestedLevel) { 315 | nestedLevel--; 316 | } 317 | } else if (ch === ":") { 318 | eatWhitespace(); 319 | if ((insideRule || enteringConditionalGroup) && 320 | !(lookBack("&") || foundNestedPseudoClass())) { 321 | // 'property: value' delimiter 322 | // which could be in a conditional group query 323 | output.push(':'); 324 | print.singleSpace(); 325 | } else { 326 | // sass/less parent reference don't use a space 327 | // sass nested pseudo-class don't use a space 328 | if (peek() === ":") { 329 | // pseudo-element 330 | next(); 331 | output.push("::"); 332 | } else { 333 | // pseudo-class 334 | output.push(':'); 335 | } 336 | } 337 | } else if (ch === '"' || ch === '\'') { 338 | if (isAfterSpace) { 339 | print.singleSpace(); 340 | } 341 | output.push(eatString(ch)); 342 | } else if (ch === ';') { 343 | output.push(ch); 344 | print.newLine(); 345 | } else if (ch === '(') { // may be a url 346 | if (lookBack("url")) { 347 | output.push(ch); 348 | eatWhitespace(); 349 | if (next()) { 350 | if (ch !== ')' && ch !== '"' && ch !== '\'') { 351 | output.push(eatString(')')); 352 | } else { 353 | pos--; 354 | } 355 | } 356 | } else { 357 | if (isAfterSpace) { 358 | print.singleSpace(); 359 | } 360 | output.push(ch); 361 | eatWhitespace(); 362 | } 363 | } else if (ch === ')') { 364 | output.push(ch); 365 | } else if (ch === ',') { 366 | output.push(ch); 367 | eatWhitespace(); 368 | if (!insideRule && selectorSeparatorNewline) { 369 | print.newLine(); 370 | } else { 371 | print.singleSpace(); 372 | } 373 | } else if (ch === ']') { 374 | output.push(ch); 375 | } else if (ch === '[') { 376 | if (isAfterSpace) { 377 | print.singleSpace(); 378 | } 379 | output.push(ch); 380 | } else if (ch === '=') { // no whitespace before or after 381 | eatWhitespace(); 382 | output.push(ch); 383 | } else { 384 | if (isAfterSpace) { 385 | print.singleSpace(); 386 | } 387 | 388 | output.push(ch); 389 | } 390 | } 391 | 392 | 393 | var sweetCode = output.join('').replace(/[\r\n\t ]+$/, ''); 394 | 395 | // establish end_with_newline 396 | if (end_with_newline) { 397 | sweetCode += "\n"; 398 | } 399 | 400 | return sweetCode; 401 | } 402 | 403 | // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule 404 | css_beautify.NESTED_AT_RULE = { 405 | "@page": true, 406 | "@font-face": true, 407 | "@keyframes": true, 408 | // also in CONDITIONAL_GROUP_RULE below 409 | "@media": true, 410 | "@supports": true, 411 | "@document": true 412 | }; 413 | css_beautify.CONDITIONAL_GROUP_RULE = { 414 | "@media": true, 415 | "@supports": true, 416 | "@document": true 417 | }; 418 | 419 | /*global define */ 420 | if (typeof define === "function" && define.amd) { 421 | // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- ) 422 | define([], function() { 423 | return { 424 | css_beautify: css_beautify 425 | }; 426 | }); 427 | } else if (typeof exports !== "undefined") { 428 | // Add support for CommonJS. Just put this file somewhere on your require.paths 429 | // and you will be able to `var html_beautify = require("beautify").html_beautify`. 430 | exports.css_beautify = css_beautify; 431 | } else if (typeof window !== "undefined") { 432 | // If we're running a web page and don't have either of the above, add our one global 433 | window.css_beautify = css_beautify; 434 | } else if (typeof global !== "undefined") { 435 | // If we don't even have window, try global. 436 | global.css_beautify = css_beautify; 437 | } 438 | 439 | }()); -------------------------------------------------------------------------------- /lib/template.js: -------------------------------------------------------------------------------- 1 | 2 | // Source: http://krasimirtsonev.com/blog/article/Javascript-template-engine-in-just-20-line 3 | 4 | var TemplateEngine = function(html, options) { 5 | var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = 'var r=[];\n', cursor = 0; 6 | var add = function(line, js) { 7 | js? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') : 8 | (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : ''); 9 | return add; 10 | } 11 | while(match = re.exec(html)) { 12 | add(html.slice(cursor, match.index))(match[1], true); 13 | cursor = match.index + match[0].length; 14 | } 15 | add(html.substr(cursor, html.length - cursor)); 16 | code += 'return r.join("");'; 17 | return new Function(code.replace(/[\r\t\n]/g, '')).apply(options); 18 | } -------------------------------------------------------------------------------- /lib/tinycolor.js: -------------------------------------------------------------------------------- 1 | // TinyColor v1.1.1 2 | // https://github.com/bgrins/TinyColor 3 | // Brian Grinstead, MIT License 4 | 5 | (function() { 6 | 7 | var trimLeft = /^[\s,#]+/, 8 | trimRight = /\s+$/, 9 | tinyCounter = 0, 10 | math = Math, 11 | mathRound = math.round, 12 | mathMin = math.min, 13 | mathMax = math.max, 14 | mathRandom = math.random; 15 | 16 | var tinycolor = function tinycolor (color, opts) { 17 | 18 | color = (color) ? color : ''; 19 | opts = opts || { }; 20 | 21 | // If input is already a tinycolor, return itself 22 | if (color instanceof tinycolor) { 23 | return color; 24 | } 25 | // If we are called as a function, call using new instead 26 | if (!(this instanceof tinycolor)) { 27 | return new tinycolor(color, opts); 28 | } 29 | 30 | var rgb = inputToRGB(color); 31 | this._originalInput = color, 32 | this._r = rgb.r, 33 | this._g = rgb.g, 34 | this._b = rgb.b, 35 | this._a = rgb.a, 36 | this._roundA = mathRound(100*this._a) / 100, 37 | this._format = opts.format || rgb.format; 38 | this._gradientType = opts.gradientType; 39 | 40 | // Don't let the range of [0,255] come back in [0,1]. 41 | // Potentially lose a little bit of precision here, but will fix issues where 42 | // .5 gets interpreted as half of the total, instead of half of 1 43 | // If it was supposed to be 128, this was already taken care of by `inputToRgb` 44 | if (this._r < 1) { this._r = mathRound(this._r); } 45 | if (this._g < 1) { this._g = mathRound(this._g); } 46 | if (this._b < 1) { this._b = mathRound(this._b); } 47 | 48 | this._ok = rgb.ok; 49 | this._tc_id = tinyCounter++; 50 | }; 51 | 52 | tinycolor.prototype = { 53 | isDark: function() { 54 | return this.getBrightness() < 128; 55 | }, 56 | isLight: function() { 57 | return !this.isDark(); 58 | }, 59 | isValid: function() { 60 | return this._ok; 61 | }, 62 | getOriginalInput: function() { 63 | return this._originalInput; 64 | }, 65 | getFormat: function() { 66 | return this._format; 67 | }, 68 | getAlpha: function() { 69 | return this._a; 70 | }, 71 | getBrightness: function() { 72 | var rgb = this.toRgb(); 73 | return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000; 74 | }, 75 | setAlpha: function(value) { 76 | this._a = boundAlpha(value); 77 | this._roundA = mathRound(100*this._a) / 100; 78 | return this; 79 | }, 80 | toHsv: function() { 81 | var hsv = rgbToHsv(this._r, this._g, this._b); 82 | return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a }; 83 | }, 84 | toHsvString: function() { 85 | var hsv = rgbToHsv(this._r, this._g, this._b); 86 | var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100); 87 | return (this._a == 1) ? 88 | "hsv(" + h + ", " + s + "%, " + v + "%)" : 89 | "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")"; 90 | }, 91 | toHsl: function() { 92 | var hsl = rgbToHsl(this._r, this._g, this._b); 93 | return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a }; 94 | }, 95 | toHslString: function() { 96 | var hsl = rgbToHsl(this._r, this._g, this._b); 97 | var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100); 98 | return (this._a == 1) ? 99 | "hsl(" + h + ", " + s + "%, " + l + "%)" : 100 | "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")"; 101 | }, 102 | toHex: function(allow3Char) { 103 | return rgbToHex(this._r, this._g, this._b, allow3Char); 104 | }, 105 | toHexString: function(allow3Char) { 106 | return '#' + this.toHex(allow3Char); 107 | }, 108 | toHex8: function() { 109 | return rgbaToHex(this._r, this._g, this._b, this._a); 110 | }, 111 | toHex8String: function() { 112 | return '#' + this.toHex8(); 113 | }, 114 | toRgb: function() { 115 | return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a }; 116 | }, 117 | toRgbString: function() { 118 | return (this._a == 1) ? 119 | "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" : 120 | "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")"; 121 | }, 122 | toPercentageRgb: function() { 123 | return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a }; 124 | }, 125 | toPercentageRgbString: function() { 126 | return (this._a == 1) ? 127 | "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" : 128 | "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")"; 129 | }, 130 | toName: function() { 131 | if (this._a === 0) { 132 | return "transparent"; 133 | } 134 | 135 | if (this._a < 1) { 136 | return false; 137 | } 138 | 139 | return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; 140 | }, 141 | toFilter: function(secondColor) { 142 | var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a); 143 | var secondHex8String = hex8String; 144 | var gradientType = this._gradientType ? "GradientType = 1, " : ""; 145 | 146 | if (secondColor) { 147 | var s = tinycolor(secondColor); 148 | secondHex8String = s.toHex8String(); 149 | } 150 | 151 | return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"; 152 | }, 153 | toString: function(format) { 154 | var formatSet = !!format; 155 | format = format || this._format; 156 | 157 | var formattedString = false; 158 | var hasAlpha = this._a < 1 && this._a >= 0; 159 | var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name"); 160 | 161 | if (needsAlphaFormat) { 162 | // Special case for "transparent", all other non-alpha formats 163 | // will return rgba when there is transparency. 164 | if (format === "name" && this._a === 0) { 165 | return this.toName(); 166 | } 167 | return this.toRgbString(); 168 | } 169 | if (format === "rgb") { 170 | formattedString = this.toRgbString(); 171 | } 172 | if (format === "prgb") { 173 | formattedString = this.toPercentageRgbString(); 174 | } 175 | if (format === "hex" || format === "hex6") { 176 | formattedString = this.toHexString(); 177 | } 178 | if (format === "hex3") { 179 | formattedString = this.toHexString(true); 180 | } 181 | if (format === "hex8") { 182 | formattedString = this.toHex8String(); 183 | } 184 | if (format === "name") { 185 | formattedString = this.toName(); 186 | } 187 | if (format === "hsl") { 188 | formattedString = this.toHslString(); 189 | } 190 | if (format === "hsv") { 191 | formattedString = this.toHsvString(); 192 | } 193 | 194 | return formattedString || this.toHexString(); 195 | }, 196 | 197 | _applyModification: function(fn, args) { 198 | var color = fn.apply(null, [this].concat([].slice.call(args))); 199 | this._r = color._r; 200 | this._g = color._g; 201 | this._b = color._b; 202 | this.setAlpha(color._a); 203 | return this; 204 | }, 205 | lighten: function() { 206 | return this._applyModification(lighten, arguments); 207 | }, 208 | brighten: function() { 209 | return this._applyModification(brighten, arguments); 210 | }, 211 | darken: function() { 212 | return this._applyModification(darken, arguments); 213 | }, 214 | desaturate: function() { 215 | return this._applyModification(desaturate, arguments); 216 | }, 217 | saturate: function() { 218 | return this._applyModification(saturate, arguments); 219 | }, 220 | greyscale: function() { 221 | return this._applyModification(greyscale, arguments); 222 | }, 223 | spin: function() { 224 | return this._applyModification(spin, arguments); 225 | }, 226 | 227 | _applyCombination: function(fn, args) { 228 | return fn.apply(null, [this].concat([].slice.call(args))); 229 | }, 230 | analogous: function() { 231 | return this._applyCombination(analogous, arguments); 232 | }, 233 | complement: function() { 234 | return this._applyCombination(complement, arguments); 235 | }, 236 | monochromatic: function() { 237 | return this._applyCombination(monochromatic, arguments); 238 | }, 239 | splitcomplement: function() { 240 | return this._applyCombination(splitcomplement, arguments); 241 | }, 242 | triad: function() { 243 | return this._applyCombination(triad, arguments); 244 | }, 245 | tetrad: function() { 246 | return this._applyCombination(tetrad, arguments); 247 | } 248 | }; 249 | 250 | // If input is an object, force 1 into "1.0" to handle ratios properly 251 | // String input requires "1.0" as input, so 1 will be treated as 1 252 | tinycolor.fromRatio = function(color, opts) { 253 | if (typeof color == "object") { 254 | var newColor = {}; 255 | for (var i in color) { 256 | if (color.hasOwnProperty(i)) { 257 | if (i === "a") { 258 | newColor[i] = color[i]; 259 | } 260 | else { 261 | newColor[i] = convertToPercentage(color[i]); 262 | } 263 | } 264 | } 265 | color = newColor; 266 | } 267 | 268 | return tinycolor(color, opts); 269 | }; 270 | 271 | // Given a string or object, convert that input to RGB 272 | // Possible string inputs: 273 | // 274 | // "red" 275 | // "#f00" or "f00" 276 | // "#ff0000" or "ff0000" 277 | // "#ff000000" or "ff000000" 278 | // "rgb 255 0 0" or "rgb (255, 0, 0)" 279 | // "rgb 1.0 0 0" or "rgb (1, 0, 0)" 280 | // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" 281 | // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" 282 | // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" 283 | // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" 284 | // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" 285 | // 286 | function inputToRGB(color) { 287 | 288 | var rgb = { r: 0, g: 0, b: 0 }; 289 | var a = 1; 290 | var ok = false; 291 | var format = false; 292 | 293 | if (typeof color == "string") { 294 | color = stringInputToObject(color); 295 | } 296 | 297 | if (typeof color == "object") { 298 | if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) { 299 | rgb = rgbToRgb(color.r, color.g, color.b); 300 | ok = true; 301 | format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; 302 | } 303 | else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) { 304 | color.s = convertToPercentage(color.s); 305 | color.v = convertToPercentage(color.v); 306 | rgb = hsvToRgb(color.h, color.s, color.v); 307 | ok = true; 308 | format = "hsv"; 309 | } 310 | else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) { 311 | color.s = convertToPercentage(color.s); 312 | color.l = convertToPercentage(color.l); 313 | rgb = hslToRgb(color.h, color.s, color.l); 314 | ok = true; 315 | format = "hsl"; 316 | } 317 | 318 | if (color.hasOwnProperty("a")) { 319 | a = color.a; 320 | } 321 | } 322 | 323 | a = boundAlpha(a); 324 | 325 | return { 326 | ok: ok, 327 | format: color.format || format, 328 | r: mathMin(255, mathMax(rgb.r, 0)), 329 | g: mathMin(255, mathMax(rgb.g, 0)), 330 | b: mathMin(255, mathMax(rgb.b, 0)), 331 | a: a 332 | }; 333 | } 334 | 335 | 336 | // Conversion Functions 337 | // -------------------- 338 | 339 | // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: 340 | // 341 | 342 | // `rgbToRgb` 343 | // Handle bounds / percentage checking to conform to CSS color spec 344 | // 345 | // *Assumes:* r, g, b in [0, 255] or [0, 1] 346 | // *Returns:* { r, g, b } in [0, 255] 347 | function rgbToRgb(r, g, b){ 348 | return { 349 | r: bound01(r, 255) * 255, 350 | g: bound01(g, 255) * 255, 351 | b: bound01(b, 255) * 255 352 | }; 353 | } 354 | 355 | // `rgbToHsl` 356 | // Converts an RGB color value to HSL. 357 | // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] 358 | // *Returns:* { h, s, l } in [0,1] 359 | function rgbToHsl(r, g, b) { 360 | 361 | r = bound01(r, 255); 362 | g = bound01(g, 255); 363 | b = bound01(b, 255); 364 | 365 | var max = mathMax(r, g, b), min = mathMin(r, g, b); 366 | var h, s, l = (max + min) / 2; 367 | 368 | if(max == min) { 369 | h = s = 0; // achromatic 370 | } 371 | else { 372 | var d = max - min; 373 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 374 | switch(max) { 375 | case r: h = (g - b) / d + (g < b ? 6 : 0); break; 376 | case g: h = (b - r) / d + 2; break; 377 | case b: h = (r - g) / d + 4; break; 378 | } 379 | 380 | h /= 6; 381 | } 382 | 383 | return { h: h, s: s, l: l }; 384 | } 385 | 386 | // `hslToRgb` 387 | // Converts an HSL color value to RGB. 388 | // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] 389 | // *Returns:* { r, g, b } in the set [0, 255] 390 | function hslToRgb(h, s, l) { 391 | var r, g, b; 392 | 393 | h = bound01(h, 360); 394 | s = bound01(s, 100); 395 | l = bound01(l, 100); 396 | 397 | function hue2rgb(p, q, t) { 398 | if(t < 0) t += 1; 399 | if(t > 1) t -= 1; 400 | if(t < 1/6) return p + (q - p) * 6 * t; 401 | if(t < 1/2) return q; 402 | if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; 403 | return p; 404 | } 405 | 406 | if(s === 0) { 407 | r = g = b = l; // achromatic 408 | } 409 | else { 410 | var q = l < 0.5 ? l * (1 + s) : l + s - l * s; 411 | var p = 2 * l - q; 412 | r = hue2rgb(p, q, h + 1/3); 413 | g = hue2rgb(p, q, h); 414 | b = hue2rgb(p, q, h - 1/3); 415 | } 416 | 417 | return { r: r * 255, g: g * 255, b: b * 255 }; 418 | } 419 | 420 | // `rgbToHsv` 421 | // Converts an RGB color value to HSV 422 | // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] 423 | // *Returns:* { h, s, v } in [0,1] 424 | function rgbToHsv(r, g, b) { 425 | 426 | r = bound01(r, 255); 427 | g = bound01(g, 255); 428 | b = bound01(b, 255); 429 | 430 | var max = mathMax(r, g, b), min = mathMin(r, g, b); 431 | var h, s, v = max; 432 | 433 | var d = max - min; 434 | s = max === 0 ? 0 : d / max; 435 | 436 | if(max == min) { 437 | h = 0; // achromatic 438 | } 439 | else { 440 | switch(max) { 441 | case r: h = (g - b) / d + (g < b ? 6 : 0); break; 442 | case g: h = (b - r) / d + 2; break; 443 | case b: h = (r - g) / d + 4; break; 444 | } 445 | h /= 6; 446 | } 447 | return { h: h, s: s, v: v }; 448 | } 449 | 450 | // `hsvToRgb` 451 | // Converts an HSV color value to RGB. 452 | // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] 453 | // *Returns:* { r, g, b } in the set [0, 255] 454 | function hsvToRgb(h, s, v) { 455 | 456 | h = bound01(h, 360) * 6; 457 | s = bound01(s, 100); 458 | v = bound01(v, 100); 459 | 460 | var i = math.floor(h), 461 | f = h - i, 462 | p = v * (1 - s), 463 | q = v * (1 - f * s), 464 | t = v * (1 - (1 - f) * s), 465 | mod = i % 6, 466 | r = [v, q, p, p, t, v][mod], 467 | g = [t, v, v, q, p, p][mod], 468 | b = [p, p, t, v, v, q][mod]; 469 | 470 | return { r: r * 255, g: g * 255, b: b * 255 }; 471 | } 472 | 473 | // `rgbToHex` 474 | // Converts an RGB color to hex 475 | // Assumes r, g, and b are contained in the set [0, 255] 476 | // Returns a 3 or 6 character hex 477 | function rgbToHex(r, g, b, allow3Char) { 478 | 479 | var hex = [ 480 | pad2(mathRound(r).toString(16)), 481 | pad2(mathRound(g).toString(16)), 482 | pad2(mathRound(b).toString(16)) 483 | ]; 484 | 485 | // Return a 3 character hex if possible 486 | if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) { 487 | return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); 488 | } 489 | 490 | return hex.join(""); 491 | } 492 | // `rgbaToHex` 493 | // Converts an RGBA color plus alpha transparency to hex 494 | // Assumes r, g, b and a are contained in the set [0, 255] 495 | // Returns an 8 character hex 496 | function rgbaToHex(r, g, b, a) { 497 | 498 | var hex = [ 499 | pad2(convertDecimalToHex(a)), 500 | pad2(mathRound(r).toString(16)), 501 | pad2(mathRound(g).toString(16)), 502 | pad2(mathRound(b).toString(16)) 503 | ]; 504 | 505 | return hex.join(""); 506 | } 507 | 508 | // `equals` 509 | // Can be called with any tinycolor input 510 | tinycolor.equals = function (color1, color2) { 511 | if (!color1 || !color2) { return false; } 512 | return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); 513 | }; 514 | tinycolor.random = function() { 515 | return tinycolor.fromRatio({ 516 | r: mathRandom(), 517 | g: mathRandom(), 518 | b: mathRandom() 519 | }); 520 | }; 521 | 522 | 523 | // Modification Functions 524 | // ---------------------- 525 | // Thanks to less.js for some of the basics here 526 | // 527 | 528 | function desaturate(color, amount) { 529 | amount = (amount === 0) ? 0 : (amount || 10); 530 | var hsl = tinycolor(color).toHsl(); 531 | hsl.s -= amount / 100; 532 | hsl.s = clamp01(hsl.s); 533 | return tinycolor(hsl); 534 | } 535 | 536 | function saturate(color, amount) { 537 | amount = (amount === 0) ? 0 : (amount || 10); 538 | var hsl = tinycolor(color).toHsl(); 539 | hsl.s += amount / 100; 540 | hsl.s = clamp01(hsl.s); 541 | return tinycolor(hsl); 542 | } 543 | 544 | function greyscale(color) { 545 | return tinycolor(color).desaturate(100); 546 | } 547 | 548 | function lighten (color, amount) { 549 | amount = (amount === 0) ? 0 : (amount || 10); 550 | var hsl = tinycolor(color).toHsl(); 551 | hsl.l += amount / 100; 552 | hsl.l = clamp01(hsl.l); 553 | return tinycolor(hsl); 554 | } 555 | 556 | function brighten(color, amount) { 557 | amount = (amount === 0) ? 0 : (amount || 10); 558 | var rgb = tinycolor(color).toRgb(); 559 | rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100)))); 560 | rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100)))); 561 | rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100)))); 562 | return tinycolor(rgb); 563 | } 564 | 565 | function darken (color, amount) { 566 | amount = (amount === 0) ? 0 : (amount || 10); 567 | var hsl = tinycolor(color).toHsl(); 568 | hsl.l -= amount / 100; 569 | hsl.l = clamp01(hsl.l); 570 | return tinycolor(hsl); 571 | } 572 | 573 | // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. 574 | // Values outside of this range will be wrapped into this range. 575 | function spin(color, amount) { 576 | var hsl = tinycolor(color).toHsl(); 577 | var hue = (mathRound(hsl.h) + amount) % 360; 578 | hsl.h = hue < 0 ? 360 + hue : hue; 579 | return tinycolor(hsl); 580 | } 581 | 582 | // Combination Functions 583 | // --------------------- 584 | // Thanks to jQuery xColor for some of the ideas behind these 585 | // 586 | 587 | function complement(color) { 588 | var hsl = tinycolor(color).toHsl(); 589 | hsl.h = (hsl.h + 180) % 360; 590 | return tinycolor(hsl); 591 | } 592 | 593 | function triad(color) { 594 | var hsl = tinycolor(color).toHsl(); 595 | var h = hsl.h; 596 | return [ 597 | tinycolor(color), 598 | tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }), 599 | tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l }) 600 | ]; 601 | } 602 | 603 | function tetrad(color) { 604 | var hsl = tinycolor(color).toHsl(); 605 | var h = hsl.h; 606 | return [ 607 | tinycolor(color), 608 | tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }), 609 | tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }), 610 | tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l }) 611 | ]; 612 | } 613 | 614 | function splitcomplement(color) { 615 | var hsl = tinycolor(color).toHsl(); 616 | var h = hsl.h; 617 | return [ 618 | tinycolor(color), 619 | tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}), 620 | tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l}) 621 | ]; 622 | } 623 | 624 | function analogous(color, results, slices) { 625 | results = results || 6; 626 | slices = slices || 30; 627 | 628 | var hsl = tinycolor(color).toHsl(); 629 | var part = 360 / slices; 630 | var ret = [tinycolor(color)]; 631 | 632 | for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) { 633 | hsl.h = (hsl.h + part) % 360; 634 | ret.push(tinycolor(hsl)); 635 | } 636 | return ret; 637 | } 638 | 639 | function monochromatic(color, results) { 640 | results = results || 6; 641 | var hsv = tinycolor(color).toHsv(); 642 | var h = hsv.h, s = hsv.s, v = hsv.v; 643 | var ret = []; 644 | var modification = 1 / results; 645 | 646 | while (results--) { 647 | ret.push(tinycolor({ h: h, s: s, v: v})); 648 | v = (v + modification) % 1; 649 | } 650 | 651 | return ret; 652 | } 653 | 654 | // Utility Functions 655 | // --------------------- 656 | 657 | tinycolor.mix = function(color1, color2, amount) { 658 | amount = (amount === 0) ? 0 : (amount || 50); 659 | 660 | var rgb1 = tinycolor(color1).toRgb(); 661 | var rgb2 = tinycolor(color2).toRgb(); 662 | 663 | var p = amount / 100; 664 | var w = p * 2 - 1; 665 | var a = rgb2.a - rgb1.a; 666 | 667 | var w1; 668 | 669 | if (w * a == -1) { 670 | w1 = w; 671 | } else { 672 | w1 = (w + a) / (1 + w * a); 673 | } 674 | 675 | w1 = (w1 + 1) / 2; 676 | 677 | var w2 = 1 - w1; 678 | 679 | var rgba = { 680 | r: rgb2.r * w1 + rgb1.r * w2, 681 | g: rgb2.g * w1 + rgb1.g * w2, 682 | b: rgb2.b * w1 + rgb1.b * w2, 683 | a: rgb2.a * p + rgb1.a * (1 - p) 684 | }; 685 | 686 | return tinycolor(rgba); 687 | }; 688 | 689 | 690 | // Readability Functions 691 | // --------------------- 692 | // 693 | 694 | // `readability` 695 | // Analyze the 2 colors and returns an object with the following properties: 696 | // `brightness`: difference in brightness between the two colors 697 | // `color`: difference in color/hue between the two colors 698 | tinycolor.readability = function(color1, color2) { 699 | var c1 = tinycolor(color1); 700 | var c2 = tinycolor(color2); 701 | var rgb1 = c1.toRgb(); 702 | var rgb2 = c2.toRgb(); 703 | var brightnessA = c1.getBrightness(); 704 | var brightnessB = c2.getBrightness(); 705 | var colorDiff = ( 706 | Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) + 707 | Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) + 708 | Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b) 709 | ); 710 | 711 | return { 712 | brightness: Math.abs(brightnessA - brightnessB), 713 | color: colorDiff 714 | }; 715 | }; 716 | 717 | // `readable` 718 | // http://www.w3.org/TR/AERT#color-contrast 719 | // Ensure that foreground and background color combinations provide sufficient contrast. 720 | // *Example* 721 | // tinycolor.isReadable("#000", "#111") => false 722 | tinycolor.isReadable = function(color1, color2) { 723 | var readability = tinycolor.readability(color1, color2); 724 | return readability.brightness > 125 && readability.color > 500; 725 | }; 726 | 727 | // `mostReadable` 728 | // Given a base color and a list of possible foreground or background 729 | // colors for that base, returns the most readable color. 730 | // *Example* 731 | // tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000" 732 | tinycolor.mostReadable = function(baseColor, colorList) { 733 | var bestColor = null; 734 | var bestScore = 0; 735 | var bestIsReadable = false; 736 | for (var i=0; i < colorList.length; i++) { 737 | 738 | // We normalize both around the "acceptable" breaking point, 739 | // but rank brightness constrast higher than hue. 740 | 741 | var readability = tinycolor.readability(baseColor, colorList[i]); 742 | var readable = readability.brightness > 125 && readability.color > 500; 743 | var score = 3 * (readability.brightness / 125) + (readability.color / 500); 744 | 745 | if ((readable && ! bestIsReadable) || 746 | (readable && bestIsReadable && score > bestScore) || 747 | ((! readable) && (! bestIsReadable) && score > bestScore)) { 748 | bestIsReadable = readable; 749 | bestScore = score; 750 | bestColor = tinycolor(colorList[i]); 751 | } 752 | } 753 | return bestColor; 754 | }; 755 | 756 | 757 | // Big List of Colors 758 | // ------------------ 759 | // 760 | var names = tinycolor.names = { 761 | aliceblue: "f0f8ff", 762 | antiquewhite: "faebd7", 763 | aqua: "0ff", 764 | aquamarine: "7fffd4", 765 | azure: "f0ffff", 766 | beige: "f5f5dc", 767 | bisque: "ffe4c4", 768 | black: "000", 769 | blanchedalmond: "ffebcd", 770 | blue: "00f", 771 | blueviolet: "8a2be2", 772 | brown: "a52a2a", 773 | burlywood: "deb887", 774 | burntsienna: "ea7e5d", 775 | cadetblue: "5f9ea0", 776 | chartreuse: "7fff00", 777 | chocolate: "d2691e", 778 | coral: "ff7f50", 779 | cornflowerblue: "6495ed", 780 | cornsilk: "fff8dc", 781 | crimson: "dc143c", 782 | cyan: "0ff", 783 | darkblue: "00008b", 784 | darkcyan: "008b8b", 785 | darkgoldenrod: "b8860b", 786 | darkgray: "a9a9a9", 787 | darkgreen: "006400", 788 | darkgrey: "a9a9a9", 789 | darkkhaki: "bdb76b", 790 | darkmagenta: "8b008b", 791 | darkolivegreen: "556b2f", 792 | darkorange: "ff8c00", 793 | darkorchid: "9932cc", 794 | darkred: "8b0000", 795 | darksalmon: "e9967a", 796 | darkseagreen: "8fbc8f", 797 | darkslateblue: "483d8b", 798 | darkslategray: "2f4f4f", 799 | darkslategrey: "2f4f4f", 800 | darkturquoise: "00ced1", 801 | darkviolet: "9400d3", 802 | deeppink: "ff1493", 803 | deepskyblue: "00bfff", 804 | dimgray: "696969", 805 | dimgrey: "696969", 806 | dodgerblue: "1e90ff", 807 | firebrick: "b22222", 808 | floralwhite: "fffaf0", 809 | forestgreen: "228b22", 810 | fuchsia: "f0f", 811 | gainsboro: "dcdcdc", 812 | ghostwhite: "f8f8ff", 813 | gold: "ffd700", 814 | goldenrod: "daa520", 815 | gray: "808080", 816 | green: "008000", 817 | greenyellow: "adff2f", 818 | grey: "808080", 819 | honeydew: "f0fff0", 820 | hotpink: "ff69b4", 821 | indianred: "cd5c5c", 822 | indigo: "4b0082", 823 | ivory: "fffff0", 824 | khaki: "f0e68c", 825 | lavender: "e6e6fa", 826 | lavenderblush: "fff0f5", 827 | lawngreen: "7cfc00", 828 | lemonchiffon: "fffacd", 829 | lightblue: "add8e6", 830 | lightcoral: "f08080", 831 | lightcyan: "e0ffff", 832 | lightgoldenrodyellow: "fafad2", 833 | lightgray: "d3d3d3", 834 | lightgreen: "90ee90", 835 | lightgrey: "d3d3d3", 836 | lightpink: "ffb6c1", 837 | lightsalmon: "ffa07a", 838 | lightseagreen: "20b2aa", 839 | lightskyblue: "87cefa", 840 | lightslategray: "789", 841 | lightslategrey: "789", 842 | lightsteelblue: "b0c4de", 843 | lightyellow: "ffffe0", 844 | lime: "0f0", 845 | limegreen: "32cd32", 846 | linen: "faf0e6", 847 | magenta: "f0f", 848 | maroon: "800000", 849 | mediumaquamarine: "66cdaa", 850 | mediumblue: "0000cd", 851 | mediumorchid: "ba55d3", 852 | mediumpurple: "9370db", 853 | mediumseagreen: "3cb371", 854 | mediumslateblue: "7b68ee", 855 | mediumspringgreen: "00fa9a", 856 | mediumturquoise: "48d1cc", 857 | mediumvioletred: "c71585", 858 | midnightblue: "191970", 859 | mintcream: "f5fffa", 860 | mistyrose: "ffe4e1", 861 | moccasin: "ffe4b5", 862 | navajowhite: "ffdead", 863 | navy: "000080", 864 | oldlace: "fdf5e6", 865 | olive: "808000", 866 | olivedrab: "6b8e23", 867 | orange: "ffa500", 868 | orangered: "ff4500", 869 | orchid: "da70d6", 870 | palegoldenrod: "eee8aa", 871 | palegreen: "98fb98", 872 | paleturquoise: "afeeee", 873 | palevioletred: "db7093", 874 | papayawhip: "ffefd5", 875 | peachpuff: "ffdab9", 876 | peru: "cd853f", 877 | pink: "ffc0cb", 878 | plum: "dda0dd", 879 | powderblue: "b0e0e6", 880 | purple: "800080", 881 | rebeccapurple: "663399", 882 | red: "f00", 883 | rosybrown: "bc8f8f", 884 | royalblue: "4169e1", 885 | saddlebrown: "8b4513", 886 | salmon: "fa8072", 887 | sandybrown: "f4a460", 888 | seagreen: "2e8b57", 889 | seashell: "fff5ee", 890 | sienna: "a0522d", 891 | silver: "c0c0c0", 892 | skyblue: "87ceeb", 893 | slateblue: "6a5acd", 894 | slategray: "708090", 895 | slategrey: "708090", 896 | snow: "fffafa", 897 | springgreen: "00ff7f", 898 | steelblue: "4682b4", 899 | tan: "d2b48c", 900 | teal: "008080", 901 | thistle: "d8bfd8", 902 | tomato: "ff6347", 903 | turquoise: "40e0d0", 904 | violet: "ee82ee", 905 | wheat: "f5deb3", 906 | white: "fff", 907 | whitesmoke: "f5f5f5", 908 | yellow: "ff0", 909 | yellowgreen: "9acd32" 910 | }; 911 | 912 | // Make it easy to access colors via `hexNames[hex]` 913 | var hexNames = tinycolor.hexNames = flip(names); 914 | 915 | 916 | // Utilities 917 | // --------- 918 | 919 | // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` 920 | function flip(o) { 921 | var flipped = { }; 922 | for (var i in o) { 923 | if (o.hasOwnProperty(i)) { 924 | flipped[o[i]] = i; 925 | } 926 | } 927 | return flipped; 928 | } 929 | 930 | // Return a valid alpha value [0,1] with all invalid values being set to 1 931 | function boundAlpha(a) { 932 | a = parseFloat(a); 933 | 934 | if (isNaN(a) || a < 0 || a > 1) { 935 | a = 1; 936 | } 937 | 938 | return a; 939 | } 940 | 941 | // Take input from [0, n] and return it as [0, 1] 942 | function bound01(n, max) { 943 | if (isOnePointZero(n)) { n = "100%"; } 944 | 945 | var processPercent = isPercentage(n); 946 | n = mathMin(max, mathMax(0, parseFloat(n))); 947 | 948 | // Automatically convert percentage into number 949 | if (processPercent) { 950 | n = parseInt(n * max, 10) / 100; 951 | } 952 | 953 | // Handle floating point rounding errors 954 | if ((math.abs(n - max) < 0.000001)) { 955 | return 1; 956 | } 957 | 958 | // Convert into [0, 1] range if it isn't already 959 | return (n % max) / parseFloat(max); 960 | } 961 | 962 | // Force a number between 0 and 1 963 | function clamp01(val) { 964 | return mathMin(1, mathMax(0, val)); 965 | } 966 | 967 | // Parse a base-16 hex value into a base-10 integer 968 | function parseIntFromHex(val) { 969 | return parseInt(val, 16); 970 | } 971 | 972 | // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 973 | // 974 | function isOnePointZero(n) { 975 | return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1; 976 | } 977 | 978 | // Check to see if string passed in is a percentage 979 | function isPercentage(n) { 980 | return typeof n === "string" && n.indexOf('%') != -1; 981 | } 982 | 983 | // Force a hex value to have 2 characters 984 | function pad2(c) { 985 | return c.length == 1 ? '0' + c : '' + c; 986 | } 987 | 988 | // Replace a decimal with it's percentage value 989 | function convertToPercentage(n) { 990 | if (n <= 1) { 991 | n = (n * 100) + "%"; 992 | } 993 | 994 | return n; 995 | } 996 | 997 | // Converts a decimal to a hex value 998 | function convertDecimalToHex(d) { 999 | return Math.round(parseFloat(d) * 255).toString(16); 1000 | } 1001 | // Converts a hex value to a decimal 1002 | function convertHexToDecimal(h) { 1003 | return (parseIntFromHex(h) / 255); 1004 | } 1005 | 1006 | var matchers = (function() { 1007 | 1008 | // 1009 | var CSS_INTEGER = "[-\\+]?\\d+%?"; 1010 | 1011 | // 1012 | var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; 1013 | 1014 | // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. 1015 | var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; 1016 | 1017 | // Actual matching. 1018 | // Parentheses and commas are optional, but not required. 1019 | // Whitespace can take the place of commas or opening paren 1020 | var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; 1021 | var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; 1022 | 1023 | return { 1024 | rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), 1025 | rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), 1026 | hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), 1027 | hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), 1028 | hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), 1029 | hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), 1030 | hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, 1031 | hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, 1032 | hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ 1033 | }; 1034 | })(); 1035 | 1036 | // `stringInputToObject` 1037 | // Permissive string parsing. Take in a number of formats, and output an object 1038 | // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` 1039 | function stringInputToObject(color) { 1040 | 1041 | color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase(); 1042 | var named = false; 1043 | if (names[color]) { 1044 | color = names[color]; 1045 | named = true; 1046 | } 1047 | else if (color == 'transparent') { 1048 | return { r: 0, g: 0, b: 0, a: 0, format: "name" }; 1049 | } 1050 | 1051 | // Try to match string input using regular expressions. 1052 | // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] 1053 | // Just return an object and let the conversion functions handle that. 1054 | // This way the result will be the same whether the tinycolor is initialized with string or object. 1055 | var match; 1056 | if ((match = matchers.rgb.exec(color))) { 1057 | return { r: match[1], g: match[2], b: match[3] }; 1058 | } 1059 | if ((match = matchers.rgba.exec(color))) { 1060 | return { r: match[1], g: match[2], b: match[3], a: match[4] }; 1061 | } 1062 | if ((match = matchers.hsl.exec(color))) { 1063 | return { h: match[1], s: match[2], l: match[3] }; 1064 | } 1065 | if ((match = matchers.hsla.exec(color))) { 1066 | return { h: match[1], s: match[2], l: match[3], a: match[4] }; 1067 | } 1068 | if ((match = matchers.hsv.exec(color))) { 1069 | return { h: match[1], s: match[2], v: match[3] }; 1070 | } 1071 | if ((match = matchers.hsva.exec(color))) { 1072 | return { h: match[1], s: match[2], v: match[3], a: match[4] }; 1073 | } 1074 | if ((match = matchers.hex8.exec(color))) { 1075 | return { 1076 | a: convertHexToDecimal(match[1]), 1077 | r: parseIntFromHex(match[2]), 1078 | g: parseIntFromHex(match[3]), 1079 | b: parseIntFromHex(match[4]), 1080 | format: named ? "name" : "hex8" 1081 | }; 1082 | } 1083 | if ((match = matchers.hex6.exec(color))) { 1084 | return { 1085 | r: parseIntFromHex(match[1]), 1086 | g: parseIntFromHex(match[2]), 1087 | b: parseIntFromHex(match[3]), 1088 | format: named ? "name" : "hex" 1089 | }; 1090 | } 1091 | if ((match = matchers.hex3.exec(color))) { 1092 | return { 1093 | r: parseIntFromHex(match[1] + '' + match[1]), 1094 | g: parseIntFromHex(match[2] + '' + match[2]), 1095 | b: parseIntFromHex(match[3] + '' + match[3]), 1096 | format: named ? "name" : "hex" 1097 | }; 1098 | } 1099 | 1100 | return false; 1101 | } 1102 | 1103 | // Node: Export function 1104 | if (typeof module !== "undefined" && module.exports) { 1105 | module.exports = tinycolor; 1106 | } 1107 | // AMD/requirejs: Define the module 1108 | else if (typeof define === 'function' && define.amd) { 1109 | define(function () {return tinycolor;}); 1110 | } 1111 | // Browser: Expose to window 1112 | else { 1113 | window.tinycolor = tinycolor; 1114 | } 1115 | 1116 | })(); -------------------------------------------------------------------------------- /src/aco2html.js: -------------------------------------------------------------------------------- 1 | /* aco2html.js - .aco photoshop palette file to HTML converter 2 | * http://websemantics.ca 3 | * 4 | * aco2html.c - .aco photoshop palette file to HTML converter 5 | * 6 | * Copyright(C) 2006 Salvatore Sanfilippo 7 | * All Rights Reserved. 8 | * 9 | * This software is released under the GPL license version 2 10 | * Read the LICENSE file in this software distribution for 11 | * more information. 12 | */ 13 | 14 | var Aco = (function() { 15 | 16 | var pointer = 0; 17 | 18 | /* Read from a file */ 19 | var fread = function(fp) 20 | { 21 | if(pointer > fp.length) 22 | return 0; 23 | 24 | return [ 25 | fp.charCodeAt(pointer++), 26 | fp.charCodeAt(pointer++) 27 | ]; 28 | } 29 | 30 | /* Read a 16bit word in big endian from 'fp' and return it 31 | * converted in the host byte order as usigned var. 32 | * On end of file -1 is returned. */ 33 | var readword = function(fp) 34 | { 35 | var buf; 36 | var w; 37 | 38 | buf = fread(fp); 39 | if (buf == 0) 40 | return -1; 41 | 42 | w = buf[1] | buf[0] << 8; 43 | return w; 44 | } 45 | 46 | /* Version of readword() that exists with an error message 47 | * if an EOF occurs */ 48 | var mustreadword = function(fp) { 49 | var w; 50 | 51 | w = readword(fp); 52 | if (w == -1) { 53 | console.log(stderr, "Unexpected end of file!\n"); 54 | return -1; 55 | } 56 | return w; 57 | } 58 | 59 | // http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb 60 | function componentToHex(c) { 61 | c = Math.floor(c); 62 | var hex = c.toString(16); 63 | return hex.length == 1 ? "0" + hex : hex; 64 | } 65 | 66 | function rgbToHex(r,g,b) { 67 | return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); 68 | } 69 | 70 | /* Convert the color read from 'fp' according to the .aco file version. 71 | * On success zero is returned and the pass-by-reference parameters 72 | * populated, otherwise non-zero is returned. 73 | * 74 | * The color name is stored in the 'name' buffer that can't 75 | * hold more than 'buflen' bytes including the nul-term. */ 76 | var convertcolor = function(fp, ver, buflen) 77 | { 78 | var cspace = mustreadword(fp); 79 | var namelen; 80 | 81 | if (cspace != 0) { 82 | var j; 83 | for (j = 0; j < 4; j++) mustreadword(fp); 84 | if (ver == 2) { 85 | mustreadword(fp); 86 | namelen = mustreadword(fp); 87 | for (j = 0; j < namelen; j++) 88 | mustreadword(fp); 89 | } 90 | console.log("Non RGB color (colorspace "+cspace+") skipped\n"); 91 | return null; 92 | } 93 | 94 | /* data in common between version 1 and 2 record */ 95 | var rgb = { 96 | r:mustreadword(fp)/256, 97 | g:mustreadword(fp)/256, 98 | b:mustreadword(fp)/256, 99 | name: null 100 | } 101 | 102 | // Add hex code 103 | rgb.hex = rgbToHex(rgb.r, rgb.g, rgb.b); 104 | 105 | mustreadword(fp); /* just skip this word, (Z not used for RGB) */ 106 | if (ver == 1) return rgb; 107 | 108 | rgb.name = []; 109 | 110 | /* version 2 specific data (name) */ 111 | 112 | mustreadword(fp); /* just skip this word, don't know what it's used for */ 113 | /* Color name, only for version 1 */ 114 | namelen = mustreadword(fp); 115 | namelen--; 116 | while(namelen > 0) { 117 | var c = mustreadword(fp); 118 | 119 | if (c > 0xff) /* To handle utf-16 here is an overkill ... */ 120 | c = ' '; 121 | if (buflen > 1) { 122 | rgb.name[rgb.name.length] = c; 123 | buflen--; 124 | } 125 | namelen--; 126 | } 127 | rgb.name='\0'; 128 | mustreadword(fp); /* Skip the nul term */ 129 | return rgb; 130 | } 131 | 132 | 133 | /* Read an ACO file from 'infp' FILE and return 134 | * the structure describing the palette. 135 | * 136 | * On initial end of file NULL is returned. 137 | * That's not a real library to read this format but just 138 | * an hack in order to write this convertion utility, so 139 | * on error we just exit(1) brutally after priting an error. */ 140 | var readaco = function(infp) 141 | { 142 | pointer = 0; 143 | 144 | var ver; 145 | var colors; 146 | var j; 147 | var aco; 148 | 149 | /* Read file version */ 150 | ver = readword(infp); 151 | 152 | if (ver == -1) return NULL; 153 | console.log("reading ACO straem version:"); 154 | if (ver == 1) { 155 | console.log(" 1 (photoshop < 7.0)\n"); 156 | } else if (ver == 2) { 157 | console.log(" 2 (photoshop >= 7.0)\n"); 158 | } else { 159 | console.log("Unknown ACO file version %d. Exiting...\n", ver); 160 | return false; 161 | } 162 | 163 | /* Read number of colors in this file */ 164 | colors = readword(infp); 165 | console.log("%d colors in this file\n", colors); 166 | 167 | /* Allocate memory */ 168 | aco = { 169 | len: colors, 170 | ver: ver, 171 | color: [] 172 | 173 | }; 174 | 175 | /* Convert every color inside */ 176 | for(var j=0; j < colors; j++) { 177 | 178 | var rgb = convertcolor(infp, ver, 256); 179 | 180 | if (rgb == null) continue; 181 | 182 | aco.color.push(rgb); 183 | 184 | } 185 | return aco; 186 | } 187 | 188 | /* Read an ACO file from 'infp' FILE and return 189 | * the hex colors palette. */ 190 | 191 | var colors = function(infp) 192 | { 193 | 194 | colors = []; 195 | 196 | var aco_struct = readaco(infp); 197 | 198 | for (var i = 0; i < aco_struct.color.length; i++) 199 | colors.push(aco_struct.color[i].hex); 200 | 201 | return colors; 202 | } 203 | 204 | return { 205 | readaco: readaco, 206 | rgbToHex:rgbToHex, 207 | colors: colors 208 | }; 209 | 210 | })(); 211 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* index.js - 2 | * 3 | * Copyright(C) 2015 Web Semantics, Inc/ 4 | * All Rights Reserved. 5 | * 6 | * This software is released under the GPL license version 2 7 | * Read the LICENSE file in this software distribution for 8 | * more information. 9 | */ 10 | 11 | var Local = (function() { 12 | 13 | var colors = [ 14 | '#fafacc', 15 | '#dfedd6', 16 | '#b5d6c9', 17 | '#d5d6b4', 18 | '#96a69a', 19 | '#839c95', 20 | '#e95d44', 21 | '#6f5f5e' 22 | ]; 23 | 24 | // 1- No shades, 2- One shade light, 3- One shade dark, 4- Two shades 25 | var shades = 1; 26 | 27 | // The lighter / darker degree of shades 28 | var shadeFactor = 10; 29 | 30 | var embed = ''; 31 | 32 | // Template for each color shades 33 | var htmlTemplate = ['
\ 34 |
\ 35 |

<%this.color_name%>

\ 36 |

#<%this.color_hex%>

\ 37 |
', 38 | '
\ 39 |
\ 40 |
\ 41 |

<%this.color_name%>

\ 42 |

#<%this.color_hex%> (master)

\ 43 |

#<%this.light_color_hex%>

\ 44 |
', 45 | '
\ 46 |
\ 47 |
\ 48 |

<%this.color_name%>

\ 49 |

#<%this.color_hex%> (master)

\ 50 |

#<%this.dark_color_hex%>

\ 51 |
', 52 | '
\ 53 |
\ 54 |
\ 55 |
\ 56 |

<%this.color_name%>

\ 57 |

#<%this.light_color_hex%>

\ 58 |

#<%this.color_hex%> (master)

\ 59 |

#<%this.dark_color_hex%>

\ 60 |
']; 61 | 62 | var cssTemplate = ['.<%this.css_class_name%> {\ 63 | background-color : #<%this.color_hex%>;\ 64 | }','.<%this.css_class_name%> {\ 65 | background-color : #<%this.color_hex%>;\ 66 | }\ 67 | .<%this.css_class_name%>-light {\ 68 | background-color : #<%this.light_color_hex%>;\ 69 | }','.<%this.css_class_name%> {\ 70 | background-color : #<%this.color_hex%>;\ 71 | }\ 72 | .<%this.css_class_name%>-dark {\ 73 | background-color : #<%this.dark_color_hex%>;\ 74 | }','.<%this.css_class_name%> {\ 75 | background-color : #<%this.color_hex%>;\ 76 | }\ 77 | .<%this.css_class_name%>-dark {\ 78 | background-color : #<%this.dark_color_hex%>;\ 79 | }\ 80 | .<%this.css_class_name%>-light {\ 81 | background-color : #<%this.light_color_hex%>;\ 82 | }']; 83 | 84 | var bodyTemplate = '
<%this.html%>
\ 85 |
\ 86 |

HTML, CSS

\ 87 |

Copy and Paste this code to your project

\ 88 |
\ 89 | \ 90 |
\ 91 | '; 92 | 93 | var box = ' .box {\ 94 | width: 128px;\ 95 | background-color: #FFFFFF;\ 96 | box-shadow: 0 2px 6px #CCC;\ 97 | border-radius: 5px;\ 98 | display: inline-block;\ 99 | margin: 10px;\ 100 | }\ 101 | \ 102 | .box h1 {\ 103 | font-weight: 400;\ 104 | font-size: 14px;\ 105 | color: #494A4A;\ 106 | display: block;\ 107 | padding: 55px 0px 5px 5px;\ 108 | margin: 0px;\ 109 | }\ 110 | \ 111 | .box p {\ 112 | font-weight: 300;\ 113 | font-size: 11px;\ 114 | color: #999999;\ 115 | display: block;\ 116 | padding: 0px 0px 5px 5px;\ 117 | margin: 0px;\ 118 | }\ 119 | \ 120 | .color-full {\ 121 | width: 128px;\ 122 | height: 50px;\ 123 | border-top-left-radius: 5px;\ 124 | border-top-right-radius: 5px;\ 125 | display: block;\ 126 | float: left;\ 127 | }\ 128 | \ 129 | .color-left {\ 130 | width: 64px;\ 131 | height: 50px;\ 132 | border-top-left-radius: 5px;\ 133 | display: block;\ 134 | float: left;\ 135 | }\ 136 | \ 137 | .color-right {\ 138 | width: 64px;\ 139 | height: 50px;\ 140 | border-top-right-radius: 5px;\ 141 | display: block;\ 142 | float: left;\ 143 | }\ 144 | \ 145 | .color-third-left {\ 146 | width: 42px;\ 147 | height: 50px;\ 148 | border-top-left-radius: 5px;\ 149 | display: block;\ 150 | float: left;\ 151 | }\ 152 | \ 153 | .color-third-middle {\ 154 | width: 44px;\ 155 | height: 50px;\ 156 | display: block;\ 157 | float: left;\ 158 | }\ 159 | \ 160 | .color-third-right {\ 161 | width: 42px;\ 162 | height: 50px;\ 163 | border-top-right-radius: 5px;\ 164 | display: block;\ 165 | float: left;\ 166 | }\ 167 | '; 168 | 169 | function beautify(source) { 170 | 171 | var output, 172 | opts = { 173 | end_with_newline : true, 174 | brace_style: true 175 | }; 176 | 177 | return html_beautify(source, opts); 178 | } 179 | 180 | function colorsToHTML() { 181 | 182 | var html = []; 183 | var css = []; 184 | 185 | for (var i = 0; i < colors.length; i++) { 186 | 187 | // Get the color 188 | var color = tinycolor(colors[i]); 189 | 190 | // Get the Human color name 191 | var n_match = ntc.name(color.toHex()); 192 | var n_rgb = n_match[0]; // RGB value of closest match 193 | var n_name = n_match[1]; // Text string: Color name 194 | var n_exactmatch = n_match[2]; // True if exact color match 195 | 196 | var color_item = { 197 | color_name : n_name, 198 | css_class_name : 'color-'+InflectionJS.dasherize(n_name.toLowerCase()), 199 | color_hex : color.toHex(), 200 | // The lighter / darker degree of shadesr).toHex() 201 | dark_color_hex : color.darken(shadeFactor).toHex(), 202 | // The lighter / darker degree of shades 203 | light_color_hex : color.lighten(shadeFactor * 2).toHex() 204 | }; 205 | 206 | html.push(TemplateEngine(htmlTemplate[shades-1], color_item)); 207 | css.push(TemplateEngine(cssTemplate[shades-1], color_item)); 208 | 209 | } 210 | 211 | return "" + html.join('\n\n'); 212 | } 213 | 214 | function showColorPalette(_colors) { 215 | 216 | colors = _colors || colors; 217 | 218 | $("#result").empty(); 219 | $("#image").empty(); 220 | 221 | var result = $("#result"); 222 | 223 | var html = colorsToHTML(); 224 | 225 | result.append(TemplateEngine(bodyTemplate, { 226 | html: html, 227 | pretty_html: beautify(embed.trim() + html) 228 | })); 229 | 230 | var editor = CodeMirror.fromTextArea(document.getElementById("code"), { 231 | extraKeys: {"Ctrl-Space": "autocomplete"}, 232 | }); 233 | 234 | } 235 | 236 | function showShade(_shades) { 237 | shades = _shades || shades; 238 | showColorPalette(); 239 | } 240 | 241 | var opts = { 242 | 'aco-file-input' : { 243 | readAsDefault: 'BinaryString', 244 | on: { 245 | load: function(e, file) { 246 | showColorPalette(Aco.colors(e.target.result)); 247 | } 248 | } 249 | }, 250 | 'image-file-input' : { 251 | on: { 252 | load: function(e, file) { 253 | 254 | var fileDiv = $("#group_" + file.extra.groupID + "_file_" + file.extra.fileID) 255 | if (file.type.match(/image/)) { 256 | // Create a thumbnail and add it to the output if it is an image 257 | var img = new Image(); 258 | img.onload = function(){ 259 | $('#image').append(img); 260 | } 261 | 262 | img.src = e.target.result; 263 | 264 | var colorThief = new ColorThief(); 265 | var _colors = colorThief.getPalette(img, 8); 266 | 267 | console.log(_colors); 268 | 269 | // Get the pallet colors 270 | colors = []; 271 | 272 | for (var i = 0; i < _colors.length; i++) { 273 | var rgb = _colors[i]; 274 | colors.push(Aco.rgbToHex(rgb[0], rgb[1], rgb[2])); 275 | }; 276 | 277 | showColorPalette(); 278 | } 279 | } 280 | } 281 | } 282 | } 283 | 284 | function setShadeFactor(factor) { 285 | shadeFactor = factor; 286 | showColorPalette(); 287 | } 288 | 289 | return { 290 | opts: opts, 291 | showShade: showShade, 292 | setShadeFactor: setShadeFactor 293 | }; 294 | 295 | })(); 296 | 297 | FileReaderJS.setupInput(document.getElementById('aco-file-input'), Local.opts['aco-file-input']); 298 | 299 | $(function() { 300 | 301 | FileReaderJS.setSync(false); 302 | 303 | $("#source").on("change", function() { 304 | 305 | $("#aco-file-input").addClass('hidden'); 306 | $("#image-file-input").addClass('hidden'); 307 | 308 | var id = $("#source").val(); 309 | 310 | // Change the File input source 311 | FileReaderJS.setupInput(document.getElementById(id), Local.opts[id]); 312 | $("#"+id).removeClass('hidden'); 313 | 314 | }); 315 | 316 | $("#clear-list").on("click", function() { 317 | $("#result").empty(); 318 | $("#image").empty(); 319 | }); 320 | 321 | $("#shades").on("change", function() { 322 | Local.showShade($("#shades").val()); 323 | }); 324 | 325 | $("#shade_factor").on("change", function() { 326 | Local.setShadeFactor($("#shade_factor").val()); 327 | }); 328 | 329 | }); --------------------------------------------------------------------------------