├── README.md ├── css ├── bootstrap.css ├── dark.css ├── font-awesome.css └── font │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff ├── index.html └── js ├── beautify-html.js ├── bootstrap-editor.js ├── bootstrap.min.js ├── class.js ├── editor.js ├── highlight.pack.js ├── jquery.hotkeys.js └── jquery.min.js /README.md: -------------------------------------------------------------------------------- 1 | ## Simple Bootstrap Editor 2 | 3 | Bootstrap themed HTML5 WYSIWYG Editor based on [boostrap-wysiwyg](https://github.com/mindmup/bootstrap-wysiwyg/) 4 | 5 | #### Demo / Docs 6 | 7 | http://frappe.github.io/simple-bootstrap-editor 8 | 9 | #### License: MIT 10 | -------------------------------------------------------------------------------- /css/dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Dark style from softwaremaniacs.org (c) Ivan Sagalaev 4 | 5 | */ 6 | 7 | pre code { 8 | display: block; padding: 0.5em; 9 | background: #444; 10 | } 11 | 12 | pre .keyword, 13 | pre .literal, 14 | pre .change, 15 | pre .winutils, 16 | pre .flow, 17 | pre .lisp .title, 18 | pre .clojure .built_in, 19 | pre .nginx .title, 20 | pre .tex .special { 21 | color: white; 22 | } 23 | 24 | pre code, 25 | pre .subst { 26 | color: #DDD; 27 | } 28 | 29 | pre .string, 30 | pre .title, 31 | pre .haskell .type, 32 | pre .ini .title, 33 | pre .tag .value, 34 | pre .css .rules .value, 35 | pre .preprocessor, 36 | pre .ruby .symbol, 37 | pre .ruby .symbol .string, 38 | pre .ruby .class .parent, 39 | pre .built_in, 40 | pre .sql .aggregate, 41 | pre .django .template_tag, 42 | pre .django .variable, 43 | pre .smalltalk .class, 44 | pre .javadoc, 45 | pre .ruby .string, 46 | pre .django .filter .argument, 47 | pre .smalltalk .localvars, 48 | pre .smalltalk .array, 49 | pre .attr_selector, 50 | pre .pseudo, 51 | pre .addition, 52 | pre .stream, 53 | pre .envvar, 54 | pre .apache .tag, 55 | pre .apache .cbracket, 56 | pre .tex .command, 57 | pre .prompt { 58 | color: #D88; 59 | } 60 | 61 | pre .comment, 62 | pre .java .annotation, 63 | pre .python .decorator, 64 | pre .template_comment, 65 | pre .pi, 66 | pre .doctype, 67 | pre .deletion, 68 | pre .shebang, 69 | pre .apache .sqbracket, 70 | pre .tex .formula { 71 | color: #777; 72 | } 73 | 74 | pre .keyword, 75 | pre .literal, 76 | pre .title, 77 | pre .css .id, 78 | pre .phpdoc, 79 | pre .haskell .type, 80 | pre .vbscript .built_in, 81 | pre .sql .aggregate, 82 | pre .rsl .built_in, 83 | pre .smalltalk .class, 84 | pre .diff .header, 85 | pre .chunk, 86 | pre .winutils, 87 | pre .bash .variable, 88 | pre .apache .tag, 89 | pre .tex .special, 90 | pre .request, 91 | pre .status { 92 | font-weight: bold; 93 | } 94 | 95 | pre .coffeescript .javascript, 96 | pre .javascript .xml, 97 | pre .tex .formula, 98 | pre .xml .javascript, 99 | pre .xml .vbscript, 100 | pre .xml .css, 101 | pre .xml .cdata { 102 | opacity: 0.5; 103 | } 104 | -------------------------------------------------------------------------------- /css/font-awesome.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 3.2.1 3 | * the iconic font designed for Bootstrap 4 | * ------------------------------------------------------------------------------ 5 | * The full suite of pictographic icons, examples, and documentation can be 6 | * found at http://fontawesome.io. Stay up to date on Twitter at 7 | * http://twitter.com/fontawesome. 8 | * 9 | * License 10 | * ------------------------------------------------------------------------------ 11 | * - The Font Awesome font is licensed under SIL OFL 1.1 - 12 | * http://scripts.sil.org/OFL 13 | * - Font Awesome CSS, LESS, and SASS files are licensed under MIT License - 14 | * http://opensource.org/licenses/mit-license.html 15 | * - Font Awesome documentation licensed under CC BY 3.0 - 16 | * http://creativecommons.org/licenses/by/3.0/ 17 | * - Attribution is no longer required in Font Awesome 3.0, but much appreciated: 18 | * "Font Awesome by Dave Gandy - http://fontawesome.io" 19 | * 20 | * Author - Dave Gandy 21 | * ------------------------------------------------------------------------------ 22 | * Email: dave@fontawesome.io 23 | * Twitter: http://twitter.com/byscuits 24 | * Work: Lead Product Designer @ Kyruus - http://kyruus.com 25 | */ 26 | /* FONT PATH 27 | * -------------------------- */ 28 | @font-face { 29 | font-family: 'FontAwesome'; 30 | src: url('font/fontawesome-webfont.eot?v=3.2.1'); 31 | src: url('font/fontawesome-webfont.eot?#iefix&v=3.2.1') format('embedded-opentype'), url('font/fontawesome-webfont.woff?v=3.2.1') format('woff'), url('font/fontawesome-webfont.ttf?v=3.2.1') format('truetype'), url('font/fontawesome-webfont.svg#fontawesomeregular?v=3.2.1') format('svg'); 32 | font-weight: normal; 33 | font-style: normal; 34 | } 35 | /* FONT AWESOME CORE 36 | * -------------------------- */ 37 | [class^="icon-"], 38 | [class*=" icon-"] { 39 | font-family: FontAwesome; 40 | font-weight: normal; 41 | font-style: normal; 42 | text-decoration: inherit; 43 | -webkit-font-smoothing: antialiased; 44 | *margin-right: .3em; 45 | } 46 | [class^="icon-"]:before, 47 | [class*=" icon-"]:before { 48 | text-decoration: inherit; 49 | display: inline-block; 50 | speak: none; 51 | } 52 | /* makes the font 33% larger relative to the icon container */ 53 | .icon-large:before { 54 | vertical-align: -10%; 55 | font-size: 1.3333333333333333em; 56 | } 57 | /* makes sure icons active on rollover in links */ 58 | a [class^="icon-"], 59 | a [class*=" icon-"] { 60 | display: inline; 61 | } 62 | /* increased font size for icon-large */ 63 | [class^="icon-"].icon-fixed-width, 64 | [class*=" icon-"].icon-fixed-width { 65 | display: inline-block; 66 | width: 1.1428571428571428em; 67 | text-align: right; 68 | padding-right: 0.2857142857142857em; 69 | } 70 | [class^="icon-"].icon-fixed-width.icon-large, 71 | [class*=" icon-"].icon-fixed-width.icon-large { 72 | width: 1.4285714285714286em; 73 | } 74 | .icons-ul { 75 | margin-left: 2.142857142857143em; 76 | list-style-type: none; 77 | } 78 | .icons-ul > li { 79 | position: relative; 80 | } 81 | .icons-ul .icon-li { 82 | position: absolute; 83 | left: -2.142857142857143em; 84 | width: 2.142857142857143em; 85 | text-align: center; 86 | line-height: inherit; 87 | } 88 | [class^="icon-"].hide, 89 | [class*=" icon-"].hide { 90 | display: none; 91 | } 92 | .icon-muted { 93 | color: #eeeeee; 94 | } 95 | .icon-light { 96 | color: #ffffff; 97 | } 98 | .icon-dark { 99 | color: #333333; 100 | } 101 | .icon-border { 102 | border: solid 1px #eeeeee; 103 | padding: .2em .25em .15em; 104 | -webkit-border-radius: 3px; 105 | -moz-border-radius: 3px; 106 | border-radius: 3px; 107 | } 108 | .icon-2x { 109 | font-size: 2em; 110 | } 111 | .icon-2x.icon-border { 112 | border-width: 2px; 113 | -webkit-border-radius: 4px; 114 | -moz-border-radius: 4px; 115 | border-radius: 4px; 116 | } 117 | .icon-3x { 118 | font-size: 3em; 119 | } 120 | .icon-3x.icon-border { 121 | border-width: 3px; 122 | -webkit-border-radius: 5px; 123 | -moz-border-radius: 5px; 124 | border-radius: 5px; 125 | } 126 | .icon-4x { 127 | font-size: 4em; 128 | } 129 | .icon-4x.icon-border { 130 | border-width: 4px; 131 | -webkit-border-radius: 6px; 132 | -moz-border-radius: 6px; 133 | border-radius: 6px; 134 | } 135 | .icon-5x { 136 | font-size: 5em; 137 | } 138 | .icon-5x.icon-border { 139 | border-width: 5px; 140 | -webkit-border-radius: 7px; 141 | -moz-border-radius: 7px; 142 | border-radius: 7px; 143 | } 144 | .pull-right { 145 | float: right; 146 | } 147 | .pull-left { 148 | float: left; 149 | } 150 | [class^="icon-"].pull-left, 151 | [class*=" icon-"].pull-left { 152 | margin-right: .3em; 153 | } 154 | [class^="icon-"].pull-right, 155 | [class*=" icon-"].pull-right { 156 | margin-left: .3em; 157 | } 158 | /* BOOTSTRAP SPECIFIC CLASSES 159 | * -------------------------- */ 160 | /* Bootstrap 2.0 sprites.less reset */ 161 | [class^="icon-"], 162 | [class*=" icon-"] { 163 | display: inline; 164 | width: auto; 165 | height: auto; 166 | line-height: normal; 167 | vertical-align: baseline; 168 | background-image: none; 169 | background-position: 0% 0%; 170 | background-repeat: repeat; 171 | margin-top: 0; 172 | } 173 | /* more sprites.less reset */ 174 | .icon-white, 175 | .nav-pills > .active > a > [class^="icon-"], 176 | .nav-pills > .active > a > [class*=" icon-"], 177 | .nav-list > .active > a > [class^="icon-"], 178 | .nav-list > .active > a > [class*=" icon-"], 179 | .navbar-inverse .nav > .active > a > [class^="icon-"], 180 | .navbar-inverse .nav > .active > a > [class*=" icon-"], 181 | .dropdown-menu > li > a:hover > [class^="icon-"], 182 | .dropdown-menu > li > a:hover > [class*=" icon-"], 183 | .dropdown-menu > .active > a > [class^="icon-"], 184 | .dropdown-menu > .active > a > [class*=" icon-"], 185 | .dropdown-submenu:hover > a > [class^="icon-"], 186 | .dropdown-submenu:hover > a > [class*=" icon-"] { 187 | background-image: none; 188 | } 189 | /* keeps Bootstrap styles with and without icons the same */ 190 | .btn [class^="icon-"].icon-large, 191 | .nav [class^="icon-"].icon-large, 192 | .btn [class*=" icon-"].icon-large, 193 | .nav [class*=" icon-"].icon-large { 194 | line-height: .9em; 195 | } 196 | .btn [class^="icon-"].icon-spin, 197 | .nav [class^="icon-"].icon-spin, 198 | .btn [class*=" icon-"].icon-spin, 199 | .nav [class*=" icon-"].icon-spin { 200 | display: inline-block; 201 | } 202 | .nav-tabs [class^="icon-"], 203 | .nav-pills [class^="icon-"], 204 | .nav-tabs [class*=" icon-"], 205 | .nav-pills [class*=" icon-"], 206 | .nav-tabs [class^="icon-"].icon-large, 207 | .nav-pills [class^="icon-"].icon-large, 208 | .nav-tabs [class*=" icon-"].icon-large, 209 | .nav-pills [class*=" icon-"].icon-large { 210 | line-height: .9em; 211 | } 212 | .btn [class^="icon-"].pull-left.icon-2x, 213 | .btn [class*=" icon-"].pull-left.icon-2x, 214 | .btn [class^="icon-"].pull-right.icon-2x, 215 | .btn [class*=" icon-"].pull-right.icon-2x { 216 | margin-top: .18em; 217 | } 218 | .btn [class^="icon-"].icon-spin.icon-large, 219 | .btn [class*=" icon-"].icon-spin.icon-large { 220 | line-height: .8em; 221 | } 222 | .btn.btn-small [class^="icon-"].pull-left.icon-2x, 223 | .btn.btn-small [class*=" icon-"].pull-left.icon-2x, 224 | .btn.btn-small [class^="icon-"].pull-right.icon-2x, 225 | .btn.btn-small [class*=" icon-"].pull-right.icon-2x { 226 | margin-top: .25em; 227 | } 228 | .btn.btn-large [class^="icon-"], 229 | .btn.btn-large [class*=" icon-"] { 230 | margin-top: 0; 231 | } 232 | .btn.btn-large [class^="icon-"].pull-left.icon-2x, 233 | .btn.btn-large [class*=" icon-"].pull-left.icon-2x, 234 | .btn.btn-large [class^="icon-"].pull-right.icon-2x, 235 | .btn.btn-large [class*=" icon-"].pull-right.icon-2x { 236 | margin-top: .05em; 237 | } 238 | .btn.btn-large [class^="icon-"].pull-left.icon-2x, 239 | .btn.btn-large [class*=" icon-"].pull-left.icon-2x { 240 | margin-right: .2em; 241 | } 242 | .btn.btn-large [class^="icon-"].pull-right.icon-2x, 243 | .btn.btn-large [class*=" icon-"].pull-right.icon-2x { 244 | margin-left: .2em; 245 | } 246 | /* Fixes alignment in nav lists */ 247 | .nav-list [class^="icon-"], 248 | .nav-list [class*=" icon-"] { 249 | line-height: inherit; 250 | } 251 | /* EXTRAS 252 | * -------------------------- */ 253 | /* Stacked and layered icon */ 254 | .icon-stack { 255 | position: relative; 256 | display: inline-block; 257 | width: 2em; 258 | height: 2em; 259 | line-height: 2em; 260 | vertical-align: -35%; 261 | } 262 | .icon-stack [class^="icon-"], 263 | .icon-stack [class*=" icon-"] { 264 | display: block; 265 | text-align: center; 266 | position: absolute; 267 | width: 100%; 268 | height: 100%; 269 | font-size: 1em; 270 | line-height: inherit; 271 | *line-height: 2em; 272 | } 273 | .icon-stack .icon-stack-base { 274 | font-size: 2em; 275 | *line-height: 1em; 276 | } 277 | /* Animated rotating icon */ 278 | .icon-spin { 279 | display: inline-block; 280 | -moz-animation: spin 2s infinite linear; 281 | -o-animation: spin 2s infinite linear; 282 | -webkit-animation: spin 2s infinite linear; 283 | animation: spin 2s infinite linear; 284 | } 285 | /* Prevent stack and spinners from being taken inline when inside a link */ 286 | a .icon-stack, 287 | a .icon-spin { 288 | display: inline-block; 289 | text-decoration: none; 290 | } 291 | @-moz-keyframes spin { 292 | 0% { 293 | -moz-transform: rotate(0deg); 294 | } 295 | 100% { 296 | -moz-transform: rotate(359deg); 297 | } 298 | } 299 | @-webkit-keyframes spin { 300 | 0% { 301 | -webkit-transform: rotate(0deg); 302 | } 303 | 100% { 304 | -webkit-transform: rotate(359deg); 305 | } 306 | } 307 | @-o-keyframes spin { 308 | 0% { 309 | -o-transform: rotate(0deg); 310 | } 311 | 100% { 312 | -o-transform: rotate(359deg); 313 | } 314 | } 315 | @-ms-keyframes spin { 316 | 0% { 317 | -ms-transform: rotate(0deg); 318 | } 319 | 100% { 320 | -ms-transform: rotate(359deg); 321 | } 322 | } 323 | @keyframes spin { 324 | 0% { 325 | transform: rotate(0deg); 326 | } 327 | 100% { 328 | transform: rotate(359deg); 329 | } 330 | } 331 | /* Icon rotations and mirroring */ 332 | .icon-rotate-90:before { 333 | -webkit-transform: rotate(90deg); 334 | -moz-transform: rotate(90deg); 335 | -ms-transform: rotate(90deg); 336 | -o-transform: rotate(90deg); 337 | transform: rotate(90deg); 338 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); 339 | } 340 | .icon-rotate-180:before { 341 | -webkit-transform: rotate(180deg); 342 | -moz-transform: rotate(180deg); 343 | -ms-transform: rotate(180deg); 344 | -o-transform: rotate(180deg); 345 | transform: rotate(180deg); 346 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); 347 | } 348 | .icon-rotate-270:before { 349 | -webkit-transform: rotate(270deg); 350 | -moz-transform: rotate(270deg); 351 | -ms-transform: rotate(270deg); 352 | -o-transform: rotate(270deg); 353 | transform: rotate(270deg); 354 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); 355 | } 356 | .icon-flip-horizontal:before { 357 | -webkit-transform: scale(-1, 1); 358 | -moz-transform: scale(-1, 1); 359 | -ms-transform: scale(-1, 1); 360 | -o-transform: scale(-1, 1); 361 | transform: scale(-1, 1); 362 | } 363 | .icon-flip-vertical:before { 364 | -webkit-transform: scale(1, -1); 365 | -moz-transform: scale(1, -1); 366 | -ms-transform: scale(1, -1); 367 | -o-transform: scale(1, -1); 368 | transform: scale(1, -1); 369 | } 370 | /* ensure rotation occurs inside anchor tags */ 371 | a .icon-rotate-90:before, 372 | a .icon-rotate-180:before, 373 | a .icon-rotate-270:before, 374 | a .icon-flip-horizontal:before, 375 | a .icon-flip-vertical:before { 376 | display: inline-block; 377 | } 378 | /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen 379 | readers do not read off random characters that represent icons */ 380 | .icon-glass:before { 381 | content: "\f000"; 382 | } 383 | .icon-music:before { 384 | content: "\f001"; 385 | } 386 | .icon-search:before { 387 | content: "\f002"; 388 | } 389 | .icon-envelope-alt:before { 390 | content: "\f003"; 391 | } 392 | .icon-heart:before { 393 | content: "\f004"; 394 | } 395 | .icon-star:before { 396 | content: "\f005"; 397 | } 398 | .icon-star-empty:before { 399 | content: "\f006"; 400 | } 401 | .icon-user:before { 402 | content: "\f007"; 403 | } 404 | .icon-film:before { 405 | content: "\f008"; 406 | } 407 | .icon-th-large:before { 408 | content: "\f009"; 409 | } 410 | .icon-th:before { 411 | content: "\f00a"; 412 | } 413 | .icon-th-list:before { 414 | content: "\f00b"; 415 | } 416 | .icon-ok:before { 417 | content: "\f00c"; 418 | } 419 | .icon-remove:before { 420 | content: "\f00d"; 421 | } 422 | .icon-zoom-in:before { 423 | content: "\f00e"; 424 | } 425 | .icon-zoom-out:before { 426 | content: "\f010"; 427 | } 428 | .icon-power-off:before, 429 | .icon-off:before { 430 | content: "\f011"; 431 | } 432 | .icon-signal:before { 433 | content: "\f012"; 434 | } 435 | .icon-gear:before, 436 | .icon-cog:before { 437 | content: "\f013"; 438 | } 439 | .icon-trash:before { 440 | content: "\f014"; 441 | } 442 | .icon-home:before { 443 | content: "\f015"; 444 | } 445 | .icon-file-alt:before { 446 | content: "\f016"; 447 | } 448 | .icon-time:before { 449 | content: "\f017"; 450 | } 451 | .icon-road:before { 452 | content: "\f018"; 453 | } 454 | .icon-download-alt:before { 455 | content: "\f019"; 456 | } 457 | .icon-download:before { 458 | content: "\f01a"; 459 | } 460 | .icon-upload:before { 461 | content: "\f01b"; 462 | } 463 | .icon-inbox:before { 464 | content: "\f01c"; 465 | } 466 | .icon-play-circle:before { 467 | content: "\f01d"; 468 | } 469 | .icon-rotate-right:before, 470 | .icon-repeat:before { 471 | content: "\f01e"; 472 | } 473 | .icon-refresh:before { 474 | content: "\f021"; 475 | } 476 | .icon-list-alt:before { 477 | content: "\f022"; 478 | } 479 | .icon-lock:before { 480 | content: "\f023"; 481 | } 482 | .icon-flag:before { 483 | content: "\f024"; 484 | } 485 | .icon-headphones:before { 486 | content: "\f025"; 487 | } 488 | .icon-volume-off:before { 489 | content: "\f026"; 490 | } 491 | .icon-volume-down:before { 492 | content: "\f027"; 493 | } 494 | .icon-volume-up:before { 495 | content: "\f028"; 496 | } 497 | .icon-qrcode:before { 498 | content: "\f029"; 499 | } 500 | .icon-barcode:before { 501 | content: "\f02a"; 502 | } 503 | .icon-tag:before { 504 | content: "\f02b"; 505 | } 506 | .icon-tags:before { 507 | content: "\f02c"; 508 | } 509 | .icon-book:before { 510 | content: "\f02d"; 511 | } 512 | .icon-bookmark:before { 513 | content: "\f02e"; 514 | } 515 | .icon-print:before { 516 | content: "\f02f"; 517 | } 518 | .icon-camera:before { 519 | content: "\f030"; 520 | } 521 | .icon-font:before { 522 | content: "\f031"; 523 | } 524 | .icon-bold:before { 525 | content: "\f032"; 526 | } 527 | .icon-italic:before { 528 | content: "\f033"; 529 | } 530 | .icon-text-height:before { 531 | content: "\f034"; 532 | } 533 | .icon-text-width:before { 534 | content: "\f035"; 535 | } 536 | .icon-align-left:before { 537 | content: "\f036"; 538 | } 539 | .icon-align-center:before { 540 | content: "\f037"; 541 | } 542 | .icon-align-right:before { 543 | content: "\f038"; 544 | } 545 | .icon-align-justify:before { 546 | content: "\f039"; 547 | } 548 | .icon-list:before { 549 | content: "\f03a"; 550 | } 551 | .icon-indent-left:before { 552 | content: "\f03b"; 553 | } 554 | .icon-indent-right:before { 555 | content: "\f03c"; 556 | } 557 | .icon-facetime-video:before { 558 | content: "\f03d"; 559 | } 560 | .icon-picture:before { 561 | content: "\f03e"; 562 | } 563 | .icon-pencil:before { 564 | content: "\f040"; 565 | } 566 | .icon-map-marker:before { 567 | content: "\f041"; 568 | } 569 | .icon-adjust:before { 570 | content: "\f042"; 571 | } 572 | .icon-tint:before { 573 | content: "\f043"; 574 | } 575 | .icon-edit:before { 576 | content: "\f044"; 577 | } 578 | .icon-share:before { 579 | content: "\f045"; 580 | } 581 | .icon-check:before { 582 | content: "\f046"; 583 | } 584 | .icon-move:before { 585 | content: "\f047"; 586 | } 587 | .icon-step-backward:before { 588 | content: "\f048"; 589 | } 590 | .icon-fast-backward:before { 591 | content: "\f049"; 592 | } 593 | .icon-backward:before { 594 | content: "\f04a"; 595 | } 596 | .icon-play:before { 597 | content: "\f04b"; 598 | } 599 | .icon-pause:before { 600 | content: "\f04c"; 601 | } 602 | .icon-stop:before { 603 | content: "\f04d"; 604 | } 605 | .icon-forward:before { 606 | content: "\f04e"; 607 | } 608 | .icon-fast-forward:before { 609 | content: "\f050"; 610 | } 611 | .icon-step-forward:before { 612 | content: "\f051"; 613 | } 614 | .icon-eject:before { 615 | content: "\f052"; 616 | } 617 | .icon-chevron-left:before { 618 | content: "\f053"; 619 | } 620 | .icon-chevron-right:before { 621 | content: "\f054"; 622 | } 623 | .icon-plus-sign:before { 624 | content: "\f055"; 625 | } 626 | .icon-minus-sign:before { 627 | content: "\f056"; 628 | } 629 | .icon-remove-sign:before { 630 | content: "\f057"; 631 | } 632 | .icon-ok-sign:before { 633 | content: "\f058"; 634 | } 635 | .icon-question-sign:before { 636 | content: "\f059"; 637 | } 638 | .icon-info-sign:before { 639 | content: "\f05a"; 640 | } 641 | .icon-screenshot:before { 642 | content: "\f05b"; 643 | } 644 | .icon-remove-circle:before { 645 | content: "\f05c"; 646 | } 647 | .icon-ok-circle:before { 648 | content: "\f05d"; 649 | } 650 | .icon-ban-circle:before { 651 | content: "\f05e"; 652 | } 653 | .icon-arrow-left:before { 654 | content: "\f060"; 655 | } 656 | .icon-arrow-right:before { 657 | content: "\f061"; 658 | } 659 | .icon-arrow-up:before { 660 | content: "\f062"; 661 | } 662 | .icon-arrow-down:before { 663 | content: "\f063"; 664 | } 665 | .icon-mail-forward:before, 666 | .icon-share-alt:before { 667 | content: "\f064"; 668 | } 669 | .icon-resize-full:before { 670 | content: "\f065"; 671 | } 672 | .icon-resize-small:before { 673 | content: "\f066"; 674 | } 675 | .icon-plus:before { 676 | content: "\f067"; 677 | } 678 | .icon-minus:before { 679 | content: "\f068"; 680 | } 681 | .icon-asterisk:before { 682 | content: "\f069"; 683 | } 684 | .icon-exclamation-sign:before { 685 | content: "\f06a"; 686 | } 687 | .icon-gift:before { 688 | content: "\f06b"; 689 | } 690 | .icon-leaf:before { 691 | content: "\f06c"; 692 | } 693 | .icon-fire:before { 694 | content: "\f06d"; 695 | } 696 | .icon-eye-open:before { 697 | content: "\f06e"; 698 | } 699 | .icon-eye-close:before { 700 | content: "\f070"; 701 | } 702 | .icon-warning-sign:before { 703 | content: "\f071"; 704 | } 705 | .icon-plane:before { 706 | content: "\f072"; 707 | } 708 | .icon-calendar:before { 709 | content: "\f073"; 710 | } 711 | .icon-random:before { 712 | content: "\f074"; 713 | } 714 | .icon-comment:before { 715 | content: "\f075"; 716 | } 717 | .icon-magnet:before { 718 | content: "\f076"; 719 | } 720 | .icon-chevron-up:before { 721 | content: "\f077"; 722 | } 723 | .icon-chevron-down:before { 724 | content: "\f078"; 725 | } 726 | .icon-retweet:before { 727 | content: "\f079"; 728 | } 729 | .icon-shopping-cart:before { 730 | content: "\f07a"; 731 | } 732 | .icon-folder-close:before { 733 | content: "\f07b"; 734 | } 735 | .icon-folder-open:before { 736 | content: "\f07c"; 737 | } 738 | .icon-resize-vertical:before { 739 | content: "\f07d"; 740 | } 741 | .icon-resize-horizontal:before { 742 | content: "\f07e"; 743 | } 744 | .icon-bar-chart:before { 745 | content: "\f080"; 746 | } 747 | .icon-twitter-sign:before { 748 | content: "\f081"; 749 | } 750 | .icon-facebook-sign:before { 751 | content: "\f082"; 752 | } 753 | .icon-camera-retro:before { 754 | content: "\f083"; 755 | } 756 | .icon-key:before { 757 | content: "\f084"; 758 | } 759 | .icon-gears:before, 760 | .icon-cogs:before { 761 | content: "\f085"; 762 | } 763 | .icon-comments:before { 764 | content: "\f086"; 765 | } 766 | .icon-thumbs-up-alt:before { 767 | content: "\f087"; 768 | } 769 | .icon-thumbs-down-alt:before { 770 | content: "\f088"; 771 | } 772 | .icon-star-half:before { 773 | content: "\f089"; 774 | } 775 | .icon-heart-empty:before { 776 | content: "\f08a"; 777 | } 778 | .icon-signout:before { 779 | content: "\f08b"; 780 | } 781 | .icon-linkedin-sign:before { 782 | content: "\f08c"; 783 | } 784 | .icon-pushpin:before { 785 | content: "\f08d"; 786 | } 787 | .icon-external-link:before { 788 | content: "\f08e"; 789 | } 790 | .icon-signin:before { 791 | content: "\f090"; 792 | } 793 | .icon-trophy:before { 794 | content: "\f091"; 795 | } 796 | .icon-github-sign:before { 797 | content: "\f092"; 798 | } 799 | .icon-upload-alt:before { 800 | content: "\f093"; 801 | } 802 | .icon-lemon:before { 803 | content: "\f094"; 804 | } 805 | .icon-phone:before { 806 | content: "\f095"; 807 | } 808 | .icon-unchecked:before, 809 | .icon-check-empty:before { 810 | content: "\f096"; 811 | } 812 | .icon-bookmark-empty:before { 813 | content: "\f097"; 814 | } 815 | .icon-phone-sign:before { 816 | content: "\f098"; 817 | } 818 | .icon-twitter:before { 819 | content: "\f099"; 820 | } 821 | .icon-facebook:before { 822 | content: "\f09a"; 823 | } 824 | .icon-github:before { 825 | content: "\f09b"; 826 | } 827 | .icon-unlock:before { 828 | content: "\f09c"; 829 | } 830 | .icon-credit-card:before { 831 | content: "\f09d"; 832 | } 833 | .icon-rss:before { 834 | content: "\f09e"; 835 | } 836 | .icon-hdd:before { 837 | content: "\f0a0"; 838 | } 839 | .icon-bullhorn:before { 840 | content: "\f0a1"; 841 | } 842 | .icon-bell:before { 843 | content: "\f0a2"; 844 | } 845 | .icon-certificate:before { 846 | content: "\f0a3"; 847 | } 848 | .icon-hand-right:before { 849 | content: "\f0a4"; 850 | } 851 | .icon-hand-left:before { 852 | content: "\f0a5"; 853 | } 854 | .icon-hand-up:before { 855 | content: "\f0a6"; 856 | } 857 | .icon-hand-down:before { 858 | content: "\f0a7"; 859 | } 860 | .icon-circle-arrow-left:before { 861 | content: "\f0a8"; 862 | } 863 | .icon-circle-arrow-right:before { 864 | content: "\f0a9"; 865 | } 866 | .icon-circle-arrow-up:before { 867 | content: "\f0aa"; 868 | } 869 | .icon-circle-arrow-down:before { 870 | content: "\f0ab"; 871 | } 872 | .icon-globe:before { 873 | content: "\f0ac"; 874 | } 875 | .icon-wrench:before { 876 | content: "\f0ad"; 877 | } 878 | .icon-tasks:before { 879 | content: "\f0ae"; 880 | } 881 | .icon-filter:before { 882 | content: "\f0b0"; 883 | } 884 | .icon-briefcase:before { 885 | content: "\f0b1"; 886 | } 887 | .icon-fullscreen:before { 888 | content: "\f0b2"; 889 | } 890 | .icon-group:before { 891 | content: "\f0c0"; 892 | } 893 | .icon-link:before { 894 | content: "\f0c1"; 895 | } 896 | .icon-cloud:before { 897 | content: "\f0c2"; 898 | } 899 | .icon-beaker:before { 900 | content: "\f0c3"; 901 | } 902 | .icon-cut:before { 903 | content: "\f0c4"; 904 | } 905 | .icon-copy:before { 906 | content: "\f0c5"; 907 | } 908 | .icon-paperclip:before, 909 | .icon-paper-clip:before { 910 | content: "\f0c6"; 911 | } 912 | .icon-save:before { 913 | content: "\f0c7"; 914 | } 915 | .icon-sign-blank:before { 916 | content: "\f0c8"; 917 | } 918 | .icon-reorder:before { 919 | content: "\f0c9"; 920 | } 921 | .icon-list-ul:before { 922 | content: "\f0ca"; 923 | } 924 | .icon-list-ol:before { 925 | content: "\f0cb"; 926 | } 927 | .icon-strikethrough:before { 928 | content: "\f0cc"; 929 | } 930 | .icon-underline:before { 931 | content: "\f0cd"; 932 | } 933 | .icon-table:before { 934 | content: "\f0ce"; 935 | } 936 | .icon-magic:before { 937 | content: "\f0d0"; 938 | } 939 | .icon-truck:before { 940 | content: "\f0d1"; 941 | } 942 | .icon-pinterest:before { 943 | content: "\f0d2"; 944 | } 945 | .icon-pinterest-sign:before { 946 | content: "\f0d3"; 947 | } 948 | .icon-google-plus-sign:before { 949 | content: "\f0d4"; 950 | } 951 | .icon-google-plus:before { 952 | content: "\f0d5"; 953 | } 954 | .icon-money:before { 955 | content: "\f0d6"; 956 | } 957 | .icon-caret-down:before { 958 | content: "\f0d7"; 959 | } 960 | .icon-caret-up:before { 961 | content: "\f0d8"; 962 | } 963 | .icon-caret-left:before { 964 | content: "\f0d9"; 965 | } 966 | .icon-caret-right:before { 967 | content: "\f0da"; 968 | } 969 | .icon-columns:before { 970 | content: "\f0db"; 971 | } 972 | .icon-sort:before { 973 | content: "\f0dc"; 974 | } 975 | .icon-sort-down:before { 976 | content: "\f0dd"; 977 | } 978 | .icon-sort-up:before { 979 | content: "\f0de"; 980 | } 981 | .icon-envelope:before { 982 | content: "\f0e0"; 983 | } 984 | .icon-linkedin:before { 985 | content: "\f0e1"; 986 | } 987 | .icon-rotate-left:before, 988 | .icon-undo:before { 989 | content: "\f0e2"; 990 | } 991 | .icon-legal:before { 992 | content: "\f0e3"; 993 | } 994 | .icon-dashboard:before { 995 | content: "\f0e4"; 996 | } 997 | .icon-comment-alt:before { 998 | content: "\f0e5"; 999 | } 1000 | .icon-comments-alt:before { 1001 | content: "\f0e6"; 1002 | } 1003 | .icon-bolt:before { 1004 | content: "\f0e7"; 1005 | } 1006 | .icon-sitemap:before { 1007 | content: "\f0e8"; 1008 | } 1009 | .icon-umbrella:before { 1010 | content: "\f0e9"; 1011 | } 1012 | .icon-paste:before { 1013 | content: "\f0ea"; 1014 | } 1015 | .icon-lightbulb:before { 1016 | content: "\f0eb"; 1017 | } 1018 | .icon-exchange:before { 1019 | content: "\f0ec"; 1020 | } 1021 | .icon-cloud-download:before { 1022 | content: "\f0ed"; 1023 | } 1024 | .icon-cloud-upload:before { 1025 | content: "\f0ee"; 1026 | } 1027 | .icon-user-md:before { 1028 | content: "\f0f0"; 1029 | } 1030 | .icon-stethoscope:before { 1031 | content: "\f0f1"; 1032 | } 1033 | .icon-suitcase:before { 1034 | content: "\f0f2"; 1035 | } 1036 | .icon-bell-alt:before { 1037 | content: "\f0f3"; 1038 | } 1039 | .icon-coffee:before { 1040 | content: "\f0f4"; 1041 | } 1042 | .icon-food:before { 1043 | content: "\f0f5"; 1044 | } 1045 | .icon-file-text-alt:before { 1046 | content: "\f0f6"; 1047 | } 1048 | .icon-building:before { 1049 | content: "\f0f7"; 1050 | } 1051 | .icon-hospital:before { 1052 | content: "\f0f8"; 1053 | } 1054 | .icon-ambulance:before { 1055 | content: "\f0f9"; 1056 | } 1057 | .icon-medkit:before { 1058 | content: "\f0fa"; 1059 | } 1060 | .icon-fighter-jet:before { 1061 | content: "\f0fb"; 1062 | } 1063 | .icon-beer:before { 1064 | content: "\f0fc"; 1065 | } 1066 | .icon-h-sign:before { 1067 | content: "\f0fd"; 1068 | } 1069 | .icon-plus-sign-alt:before { 1070 | content: "\f0fe"; 1071 | } 1072 | .icon-double-angle-left:before { 1073 | content: "\f100"; 1074 | } 1075 | .icon-double-angle-right:before { 1076 | content: "\f101"; 1077 | } 1078 | .icon-double-angle-up:before { 1079 | content: "\f102"; 1080 | } 1081 | .icon-double-angle-down:before { 1082 | content: "\f103"; 1083 | } 1084 | .icon-angle-left:before { 1085 | content: "\f104"; 1086 | } 1087 | .icon-angle-right:before { 1088 | content: "\f105"; 1089 | } 1090 | .icon-angle-up:before { 1091 | content: "\f106"; 1092 | } 1093 | .icon-angle-down:before { 1094 | content: "\f107"; 1095 | } 1096 | .icon-desktop:before { 1097 | content: "\f108"; 1098 | } 1099 | .icon-laptop:before { 1100 | content: "\f109"; 1101 | } 1102 | .icon-tablet:before { 1103 | content: "\f10a"; 1104 | } 1105 | .icon-mobile-phone:before { 1106 | content: "\f10b"; 1107 | } 1108 | .icon-circle-blank:before { 1109 | content: "\f10c"; 1110 | } 1111 | .icon-quote-left:before { 1112 | content: "\f10d"; 1113 | } 1114 | .icon-quote-right:before { 1115 | content: "\f10e"; 1116 | } 1117 | .icon-spinner:before { 1118 | content: "\f110"; 1119 | } 1120 | .icon-circle:before { 1121 | content: "\f111"; 1122 | } 1123 | .icon-mail-reply:before, 1124 | .icon-reply:before { 1125 | content: "\f112"; 1126 | } 1127 | .icon-github-alt:before { 1128 | content: "\f113"; 1129 | } 1130 | .icon-folder-close-alt:before { 1131 | content: "\f114"; 1132 | } 1133 | .icon-folder-open-alt:before { 1134 | content: "\f115"; 1135 | } 1136 | .icon-expand-alt:before { 1137 | content: "\f116"; 1138 | } 1139 | .icon-collapse-alt:before { 1140 | content: "\f117"; 1141 | } 1142 | .icon-smile:before { 1143 | content: "\f118"; 1144 | } 1145 | .icon-frown:before { 1146 | content: "\f119"; 1147 | } 1148 | .icon-meh:before { 1149 | content: "\f11a"; 1150 | } 1151 | .icon-gamepad:before { 1152 | content: "\f11b"; 1153 | } 1154 | .icon-keyboard:before { 1155 | content: "\f11c"; 1156 | } 1157 | .icon-flag-alt:before { 1158 | content: "\f11d"; 1159 | } 1160 | .icon-flag-checkered:before { 1161 | content: "\f11e"; 1162 | } 1163 | .icon-terminal:before { 1164 | content: "\f120"; 1165 | } 1166 | .icon-code:before { 1167 | content: "\f121"; 1168 | } 1169 | .icon-reply-all:before { 1170 | content: "\f122"; 1171 | } 1172 | .icon-mail-reply-all:before { 1173 | content: "\f122"; 1174 | } 1175 | .icon-star-half-full:before, 1176 | .icon-star-half-empty:before { 1177 | content: "\f123"; 1178 | } 1179 | .icon-location-arrow:before { 1180 | content: "\f124"; 1181 | } 1182 | .icon-crop:before { 1183 | content: "\f125"; 1184 | } 1185 | .icon-code-fork:before { 1186 | content: "\f126"; 1187 | } 1188 | .icon-unlink:before { 1189 | content: "\f127"; 1190 | } 1191 | .icon-question:before { 1192 | content: "\f128"; 1193 | } 1194 | .icon-info:before { 1195 | content: "\f129"; 1196 | } 1197 | .icon-exclamation:before { 1198 | content: "\f12a"; 1199 | } 1200 | .icon-superscript:before { 1201 | content: "\f12b"; 1202 | } 1203 | .icon-subscript:before { 1204 | content: "\f12c"; 1205 | } 1206 | .icon-eraser:before { 1207 | content: "\f12d"; 1208 | } 1209 | .icon-puzzle-piece:before { 1210 | content: "\f12e"; 1211 | } 1212 | .icon-microphone:before { 1213 | content: "\f130"; 1214 | } 1215 | .icon-microphone-off:before { 1216 | content: "\f131"; 1217 | } 1218 | .icon-shield:before { 1219 | content: "\f132"; 1220 | } 1221 | .icon-calendar-empty:before { 1222 | content: "\f133"; 1223 | } 1224 | .icon-fire-extinguisher:before { 1225 | content: "\f134"; 1226 | } 1227 | .icon-rocket:before { 1228 | content: "\f135"; 1229 | } 1230 | .icon-maxcdn:before { 1231 | content: "\f136"; 1232 | } 1233 | .icon-chevron-sign-left:before { 1234 | content: "\f137"; 1235 | } 1236 | .icon-chevron-sign-right:before { 1237 | content: "\f138"; 1238 | } 1239 | .icon-chevron-sign-up:before { 1240 | content: "\f139"; 1241 | } 1242 | .icon-chevron-sign-down:before { 1243 | content: "\f13a"; 1244 | } 1245 | .icon-html5:before { 1246 | content: "\f13b"; 1247 | } 1248 | .icon-css3:before { 1249 | content: "\f13c"; 1250 | } 1251 | .icon-anchor:before { 1252 | content: "\f13d"; 1253 | } 1254 | .icon-unlock-alt:before { 1255 | content: "\f13e"; 1256 | } 1257 | .icon-bullseye:before { 1258 | content: "\f140"; 1259 | } 1260 | .icon-ellipsis-horizontal:before { 1261 | content: "\f141"; 1262 | } 1263 | .icon-ellipsis-vertical:before { 1264 | content: "\f142"; 1265 | } 1266 | .icon-rss-sign:before { 1267 | content: "\f143"; 1268 | } 1269 | .icon-play-sign:before { 1270 | content: "\f144"; 1271 | } 1272 | .icon-ticket:before { 1273 | content: "\f145"; 1274 | } 1275 | .icon-minus-sign-alt:before { 1276 | content: "\f146"; 1277 | } 1278 | .icon-check-minus:before { 1279 | content: "\f147"; 1280 | } 1281 | .icon-level-up:before { 1282 | content: "\f148"; 1283 | } 1284 | .icon-level-down:before { 1285 | content: "\f149"; 1286 | } 1287 | .icon-check-sign:before { 1288 | content: "\f14a"; 1289 | } 1290 | .icon-edit-sign:before { 1291 | content: "\f14b"; 1292 | } 1293 | .icon-external-link-sign:before { 1294 | content: "\f14c"; 1295 | } 1296 | .icon-share-sign:before { 1297 | content: "\f14d"; 1298 | } 1299 | .icon-compass:before { 1300 | content: "\f14e"; 1301 | } 1302 | .icon-collapse:before { 1303 | content: "\f150"; 1304 | } 1305 | .icon-collapse-top:before { 1306 | content: "\f151"; 1307 | } 1308 | .icon-expand:before { 1309 | content: "\f152"; 1310 | } 1311 | .icon-euro:before, 1312 | .icon-eur:before { 1313 | content: "\f153"; 1314 | } 1315 | .icon-gbp:before { 1316 | content: "\f154"; 1317 | } 1318 | .icon-dollar:before, 1319 | .icon-usd:before { 1320 | content: "\f155"; 1321 | } 1322 | .icon-rupee:before, 1323 | .icon-inr:before { 1324 | content: "\f156"; 1325 | } 1326 | .icon-yen:before, 1327 | .icon-jpy:before { 1328 | content: "\f157"; 1329 | } 1330 | .icon-renminbi:before, 1331 | .icon-cny:before { 1332 | content: "\f158"; 1333 | } 1334 | .icon-won:before, 1335 | .icon-krw:before { 1336 | content: "\f159"; 1337 | } 1338 | .icon-bitcoin:before, 1339 | .icon-btc:before { 1340 | content: "\f15a"; 1341 | } 1342 | .icon-file:before { 1343 | content: "\f15b"; 1344 | } 1345 | .icon-file-text:before { 1346 | content: "\f15c"; 1347 | } 1348 | .icon-sort-by-alphabet:before { 1349 | content: "\f15d"; 1350 | } 1351 | .icon-sort-by-alphabet-alt:before { 1352 | content: "\f15e"; 1353 | } 1354 | .icon-sort-by-attributes:before { 1355 | content: "\f160"; 1356 | } 1357 | .icon-sort-by-attributes-alt:before { 1358 | content: "\f161"; 1359 | } 1360 | .icon-sort-by-order:before { 1361 | content: "\f162"; 1362 | } 1363 | .icon-sort-by-order-alt:before { 1364 | content: "\f163"; 1365 | } 1366 | .icon-thumbs-up:before { 1367 | content: "\f164"; 1368 | } 1369 | .icon-thumbs-down:before { 1370 | content: "\f165"; 1371 | } 1372 | .icon-youtube-sign:before { 1373 | content: "\f166"; 1374 | } 1375 | .icon-youtube:before { 1376 | content: "\f167"; 1377 | } 1378 | .icon-xing:before { 1379 | content: "\f168"; 1380 | } 1381 | .icon-xing-sign:before { 1382 | content: "\f169"; 1383 | } 1384 | .icon-youtube-play:before { 1385 | content: "\f16a"; 1386 | } 1387 | .icon-dropbox:before { 1388 | content: "\f16b"; 1389 | } 1390 | .icon-stackexchange:before { 1391 | content: "\f16c"; 1392 | } 1393 | .icon-instagram:before { 1394 | content: "\f16d"; 1395 | } 1396 | .icon-flickr:before { 1397 | content: "\f16e"; 1398 | } 1399 | .icon-adn:before { 1400 | content: "\f170"; 1401 | } 1402 | .icon-bitbucket:before { 1403 | content: "\f171"; 1404 | } 1405 | .icon-bitbucket-sign:before { 1406 | content: "\f172"; 1407 | } 1408 | .icon-tumblr:before { 1409 | content: "\f173"; 1410 | } 1411 | .icon-tumblr-sign:before { 1412 | content: "\f174"; 1413 | } 1414 | .icon-long-arrow-down:before { 1415 | content: "\f175"; 1416 | } 1417 | .icon-long-arrow-up:before { 1418 | content: "\f176"; 1419 | } 1420 | .icon-long-arrow-left:before { 1421 | content: "\f177"; 1422 | } 1423 | .icon-long-arrow-right:before { 1424 | content: "\f178"; 1425 | } 1426 | .icon-apple:before { 1427 | content: "\f179"; 1428 | } 1429 | .icon-windows:before { 1430 | content: "\f17a"; 1431 | } 1432 | .icon-android:before { 1433 | content: "\f17b"; 1434 | } 1435 | .icon-linux:before { 1436 | content: "\f17c"; 1437 | } 1438 | .icon-dribbble:before { 1439 | content: "\f17d"; 1440 | } 1441 | .icon-skype:before { 1442 | content: "\f17e"; 1443 | } 1444 | .icon-foursquare:before { 1445 | content: "\f180"; 1446 | } 1447 | .icon-trello:before { 1448 | content: "\f181"; 1449 | } 1450 | .icon-female:before { 1451 | content: "\f182"; 1452 | } 1453 | .icon-male:before { 1454 | content: "\f183"; 1455 | } 1456 | .icon-gittip:before { 1457 | content: "\f184"; 1458 | } 1459 | .icon-sun:before { 1460 | content: "\f185"; 1461 | } 1462 | .icon-moon:before { 1463 | content: "\f186"; 1464 | } 1465 | .icon-archive:before { 1466 | content: "\f187"; 1467 | } 1468 | .icon-bug:before { 1469 | content: "\f188"; 1470 | } 1471 | .icon-vk:before { 1472 | content: "\f189"; 1473 | } 1474 | .icon-weibo:before { 1475 | content: "\f18a"; 1476 | } 1477 | .icon-renren:before { 1478 | content: "\f18b"; 1479 | } 1480 | -------------------------------------------------------------------------------- /css/font/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/simple-bootstrap-editor/8f65e2f204243f05a0e1910abfe3ce7dbb343d7e/css/font/FontAwesome.otf -------------------------------------------------------------------------------- /css/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/simple-bootstrap-editor/8f65e2f204243f05a0e1910abfe3ce7dbb343d7e/css/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /css/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/simple-bootstrap-editor/8f65e2f204243f05a0e1910abfe3ce7dbb343d7e/css/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /css/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/simple-bootstrap-editor/8f65e2f204243f05a0e1910abfe3ce7dbb343d7e/css/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Simple Bootstrap Editor 0.1 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 47 | 48 |
49 | 54 |
55 |
56 |
57 |

58 |

Bootstrap Based HTML5 WYSIWYG Editor

59 |

Click me to edit!

60 |
61 |

The Problem

62 |
63 |

There are many text editors out there, yet nothing feels right:

64 |
    65 |
  • TinyMCE, CKEdit etc: For industrial strength editing.
  • 66 |
  • Aloha, Mercury: Web 2.0, but still missing something.
  • 67 |
  • Boostrap WYSIWYG By Mindmup: Nice but too light.
  • 68 |
69 |

Here is the feature list we wanted:

70 |
    71 |
  • Bootstrap themed
  • 72 |
  • Lightweight and easy to configure
  • 73 |
  • For fixed (contenteditable) + inline editing
  • 74 |
  • HTML editing
  • 75 |
  • Image insertion
  • 76 |
77 |
78 |

The Solution

79 |
80 |

We built a simple editor based on Boostrap WYSIWYG By Mindmup and added

81 |
    82 |
  • HTML editing and beautify_html
  • 83 |
  • Inline and fixed options
  • 84 |
  • Simple API
  • 85 |
  • Extendable
  • 86 |
87 |
88 |

89 | 90 |

91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |

Dependencies (bundled in repository)

99 |
    100 |
  • Bootstrap
  • 101 |
  • JQuery
  • 102 |
  • JQuery.Hotkeys
  • 103 |
  • Beautify HTML
  • 104 |
  • Class (by John Resig)
  • 105 |
106 |
107 |

Fixed Editor

108 |

Pass editor option with content to be edited

109 |
// fixed editor
110 | var fixed = new bsEditor({
111 | 	editor: $(".editable-content"),
112 | 	toolbar_style: {
113 | 		"background-color": "black"
114 | 	}
115 | });
116 |
117 |

Inline Editor

118 |

Pass parent option pointing to the parent element.

119 |
// inline
120 | var inline = new bsEditor({
121 | 	parent: $("#inline-parent"),
122 | });
123 | inline.set_input("Write Something!")
124 | 
125 |
126 |

Options

127 |
128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 |
OptionDescription
editorFor fixed mode, this element will become contenteditable
parentFor inline mode, make editor in this element
toolbar_styleCSS for toolbars
onsaveFunction to be called on saving (fixed mode)
oncancelFunction to be called on cancel (fixed mode)
changeFunction to be called on change (inline mode)
162 |
163 |

Methods

164 |
165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 |
MethodDescription
set_input(html)Set HTML in Editor
get_value()Get Inner HTML
183 |
184 |
185 |
186 | 208 | 209 | Fork me on GitHub 210 | 211 | 212 |
213 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /js/beautify-html.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 | Style HTML 30 | --------------- 31 | 32 | Written by Nochum Sossonko, (nsossonko@hotmail.com) 33 | 34 | Based on code initially developed by: Einar Lielmanis, 35 | http://jsbeautifier.org/ 36 | 37 | Usage: 38 | style_html(html_source); 39 | 40 | style_html(html_source, options); 41 | 42 | The options are: 43 | indent_size (default 4) — indentation size, 44 | indent_char (default space) — character to indent with, 45 | max_char (default 250) - maximum amount of characters per line (0 = disable) 46 | brace_style (default "collapse") - "collapse" | "expand" | "end-expand" 47 | put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line. 48 | unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted 49 | indent_scripts (default normal) - "keep"|"separate"|"normal" 50 | 51 | e.g. 52 | 53 | style_html(html_source, { 54 | 'indent_size': 2, 55 | 'indent_char': ' ', 56 | 'max_char': 78, 57 | 'brace_style': 'expand', 58 | 'unformatted': ['a', 'sub', 'sup', 'b', 'i', 'u'] 59 | }); 60 | */ 61 | 62 | (function() { 63 | 64 | function style_html(html_source, options, js_beautify, css_beautify) { 65 | //Wrapper function to invoke all the necessary constructors and deal with the output. 66 | 67 | var multi_parser, 68 | indent_size, 69 | indent_character, 70 | max_char, 71 | brace_style, 72 | unformatted; 73 | 74 | options = options || {}; 75 | indent_size = options.indent_size || 4; 76 | indent_character = options.indent_char || ' '; 77 | brace_style = options.brace_style || 'collapse'; 78 | max_char = options.max_char === 0 ? Infinity : options.max_char || 250; 79 | unformatted = options.unformatted || ['a', 'span', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike', 'font', 'ins', 'del', 'pre', 'address', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']; 80 | 81 | function Parser() { 82 | 83 | this.pos = 0; //Parser position 84 | this.token = ''; 85 | this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT 86 | this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values 87 | parent: 'parent1', 88 | parentcount: 1, 89 | parent1: '' 90 | }; 91 | this.tag_type = ''; 92 | this.token_text = this.last_token = this.last_text = this.token_type = ''; 93 | 94 | this.Utils = { //Uilities made available to the various functions 95 | whitespace: "\n\r\t ".split(''), 96 | single_token: 'br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed,?php,?,?='.split(','), //all the single tags for HTML 97 | extra_liners: 'head,body,/html'.split(','), //for tags that need a line of whitespace before them 98 | in_array: function (what, arr) { 99 | for (var i=0; i= this.input.length) { 116 | return content.length?content.join(''):['', 'TK_EOF']; 117 | } 118 | 119 | input_char = this.input.charAt(this.pos); 120 | this.pos++; 121 | this.line_char_count++; 122 | 123 | if (this.Utils.in_array(input_char, this.Utils.whitespace)) { 124 | if (content.length) { 125 | space = true; 126 | } 127 | this.line_char_count--; 128 | continue; //don't want to insert unnecessary space 129 | } 130 | else if (space) { 131 | if (this.line_char_count >= this.max_char) { //insert a line when the max_char is reached 132 | content.push('\n'); 133 | for (var i=0; i', 'igm'); 156 | reg_match.lastIndex = this.pos; 157 | var reg_array = reg_match.exec(this.input); 158 | var end_script = reg_array?reg_array.index:this.input.length; //absolute end of script 159 | if(this.pos < end_script) { //get everything in between the script tags 160 | content = this.input.substring(this.pos, end_script); 161 | this.pos = end_script; 162 | } 163 | return content; 164 | }; 165 | 166 | this.record_tag = function (tag){ //function to record a tag and its parent in this.tags Object 167 | if (this.tags[tag + 'count']) { //check for the existence of this tag type 168 | this.tags[tag + 'count']++; 169 | this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level 170 | } 171 | else { //otherwise initialize this tag type 172 | this.tags[tag + 'count'] = 1; 173 | this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level 174 | } 175 | this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent) 176 | this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1') 177 | }; 178 | 179 | this.retrieve_tag = function (tag) { //function to retrieve the opening tag to the corresponding closer 180 | if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it 181 | var temp_parent = this.tags.parent; //check to see if it's a closable tag. 182 | while (temp_parent) { //till we reach '' (the initial value); 183 | if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it 184 | break; 185 | } 186 | temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree 187 | } 188 | if (temp_parent) { //if we caught something 189 | this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly 190 | this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent 191 | } 192 | delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference... 193 | delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself 194 | if (this.tags[tag + 'count'] === 1) { 195 | delete this.tags[tag + 'count']; 196 | } 197 | else { 198 | this.tags[tag + 'count']--; 199 | } 200 | } 201 | }; 202 | 203 | this.get_tag = function (peek) { //function to get a full tag and parse its type 204 | var input_char = '', 205 | content = [], 206 | comment = '', 207 | space = false, 208 | tag_start, tag_end, 209 | orig_pos = this.pos, 210 | orig_line_char_count = this.line_char_count; 211 | 212 | peek = peek !== undefined ? peek : false; 213 | 214 | do { 215 | if (this.pos >= this.input.length) { 216 | if (peek) { 217 | this.pos = orig_pos; 218 | this.line_char_count = orig_line_char_count; 219 | } 220 | return content.length?content.join(''):['', 'TK_EOF']; 221 | } 222 | 223 | input_char = this.input.charAt(this.pos); 224 | this.pos++; 225 | this.line_char_count++; 226 | 227 | if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space 228 | space = true; 229 | this.line_char_count--; 230 | continue; 231 | } 232 | 233 | if (input_char === "'" || input_char === '"') { 234 | if (!content[1] || content[1] !== '!') { //if we're in a comment strings don't get treated specially 235 | input_char += this.get_unformatted(input_char); 236 | space = true; 237 | } 238 | } 239 | 240 | if (input_char === '=') { //no space before = 241 | space = false; 242 | } 243 | 244 | if (content.length && content[content.length-1] !== '=' && input_char !== '>' && space) { 245 | //no space after = or before > 246 | if (this.line_char_count >= this.max_char) { 247 | this.print_newline(false, content); 248 | this.line_char_count = 0; 249 | } 250 | else { 251 | content.push(' '); 252 | this.line_char_count++; 253 | } 254 | space = false; 255 | } 256 | if (input_char === '<') { 257 | tag_start = this.pos - 1; 258 | } 259 | content.push(input_char); //inserts character at-a-time (or string) 260 | } while (input_char !== '>'); 261 | 262 | var tag_complete = content.join(''); 263 | var tag_index; 264 | if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends 265 | tag_index = tag_complete.indexOf(' '); 266 | } 267 | else { //otherwise go with the tag ending 268 | tag_index = tag_complete.indexOf('>'); 269 | } 270 | var tag_check = tag_complete.substring(1, tag_index).toLowerCase(); 271 | if (tag_complete.charAt(tag_complete.length-2) === '/' || 272 | this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /) 273 | if ( ! peek) { 274 | this.tag_type = 'SINGLE'; 275 | } 276 | } 277 | else if (tag_check === 'script') { //for later script handling 278 | if ( ! peek) { 279 | this.record_tag(tag_check); 280 | this.tag_type = 'SCRIPT'; 281 | } 282 | } 283 | else if (tag_check === 'style') { //for future style handling (for now it justs uses get_content) 284 | if ( ! peek) { 285 | this.record_tag(tag_check); 286 | this.tag_type = 'STYLE'; 287 | } 288 | } 289 | else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags 290 | comment = this.get_unformatted('', tag_complete); //...delegate to get_unformatted function 291 | content.push(comment); 292 | // Preserve collapsed whitespace either before or after this tag. 293 | if (tag_start > 0 && this.Utils.in_array(this.input.charAt(tag_start - 1), this.Utils.whitespace)){ 294 | content.splice(0, 0, this.input.charAt(tag_start - 1)); 295 | } 296 | tag_end = this.pos - 1; 297 | if (this.Utils.in_array(this.input.charAt(tag_end + 1), this.Utils.whitespace)){ 298 | content.push(this.input.charAt(tag_end + 1)); 299 | } 300 | this.tag_type = 'SINGLE'; 301 | } 302 | else if (tag_check.charAt(0) === '!') { //peek for so... 305 | comment = this.get_unformatted('-->', tag_complete); //...delegate to get_unformatted 306 | content.push(comment); 307 | } 308 | if ( ! peek) { 309 | this.tag_type = 'START'; 310 | } 311 | } 312 | else if (tag_check.indexOf('[endif') !== -1) {//peek for ', tag_complete); 325 | content.push(comment); 326 | this.tag_type = 'SINGLE'; 327 | } 328 | } 329 | else if ( ! peek) { 330 | if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending 331 | this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors 332 | this.tag_type = 'END'; 333 | } 334 | else { //otherwise it's a start-tag 335 | this.record_tag(tag_check); //push it on the tag stack 336 | this.tag_type = 'START'; 337 | } 338 | if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line 339 | this.print_newline(true, this.output); 340 | } 341 | } 342 | 343 | if (peek) { 344 | this.pos = orig_pos; 345 | this.line_char_count = orig_line_char_count; 346 | } 347 | 348 | return content.join(''); //returns fully formatted tag 349 | }; 350 | 351 | this.get_unformatted = function (delimiter, orig_tag) { //function to return unformatted content in its entirety 352 | 353 | if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) { 354 | return ''; 355 | } 356 | var input_char = ''; 357 | var content = ''; 358 | var space = true; 359 | do { 360 | 361 | if (this.pos >= this.input.length) { 362 | return content; 363 | } 364 | 365 | input_char = this.input.charAt(this.pos); 366 | this.pos++; 367 | 368 | if (this.Utils.in_array(input_char, this.Utils.whitespace)) { 369 | if (!space) { 370 | this.line_char_count--; 371 | continue; 372 | } 373 | if (input_char === '\n' || input_char === '\r') { 374 | content += '\n'; 375 | /* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect
 tags if they are specified in the 'unformatted array'
376 |                 for (var i=0; i]*>\s*$/);
452 | 
453 |             // if next_tag comes back but is not an isolated tag, then
454 |             // let's treat the 'a' tag as having content
455 |             // and respect the unformatted option
456 |             if (!tag || this.Utils.in_array(tag, unformatted)){
457 |                 return true;
458 |             } else {
459 |                 return false;
460 |             }
461 |         };
462 | 
463 |         this.printer = function (js_source, indent_character, indent_size, max_char, brace_style) { //handles input/output and some other printing functions
464 | 
465 |           this.input = js_source || ''; //gets the input for the Parser
466 |           this.output = [];
467 |           this.indent_character = indent_character;
468 |           this.indent_string = '';
469 |           this.indent_size = indent_size;
470 |           this.brace_style = brace_style;
471 |           this.indent_level = 0;
472 |           this.max_char = max_char;
473 |           this.line_char_count = 0; //count to see if max_char was exceeded
474 | 
475 |           for (var i=0; i 0) {
505 |               this.indent_level--;
506 |             }
507 |           };
508 |         };
509 |         return this;
510 |       }
511 | 
512 |       /*_____________________--------------------_____________________*/
513 | 
514 |       multi_parser = new Parser(); //wrapping functions Parser
515 |       multi_parser.printer(html_source, indent_character, indent_size, max_char, brace_style); //initialize starting values
516 | 
517 |       while (true) {
518 |           var t = multi_parser.get_token();
519 |           multi_parser.token_text = t[0];
520 |           multi_parser.token_type = t[1];
521 | 
522 |         if (multi_parser.token_type === 'TK_EOF') {
523 |           break;
524 |         }
525 | 
526 |         switch (multi_parser.token_type) {
527 |           case 'TK_TAG_START':
528 |             multi_parser.print_newline(false, multi_parser.output);
529 |             multi_parser.print_token(multi_parser.token_text);
530 |             multi_parser.indent();
531 |             multi_parser.current_mode = 'CONTENT';
532 |             break;
533 |           case 'TK_TAG_STYLE':
534 |           case 'TK_TAG_SCRIPT':
535 |             multi_parser.print_newline(false, multi_parser.output);
536 |             multi_parser.print_token(multi_parser.token_text);
537 |             multi_parser.current_mode = 'CONTENT';
538 |             break;
539 |           case 'TK_TAG_END':
540 |             //Print new line only if the tag has no content and has child
541 |             if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
542 |                 var tag_name = multi_parser.token_text.match(/\w+/)[0];
543 |                 var tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length -1].match(/<\s*(\w+)/);
544 |                 if (tag_extracted_from_last_output === null || tag_extracted_from_last_output[1] !== tag_name) {
545 |                     multi_parser.print_newline(true, multi_parser.output);
546 |                 }
547 |             }
548 |             multi_parser.print_token(multi_parser.token_text);
549 |             multi_parser.current_mode = 'CONTENT';
550 |             break;
551 |           case 'TK_TAG_SINGLE':
552 |             // Don't add a newline before elements that should remain unformatted.
553 |             var tag_check = multi_parser.token_text.match(/^\s*<([a-z]+)/i);
554 |             if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)){
555 |                 multi_parser.print_newline(false, multi_parser.output);
556 |             }
557 |             multi_parser.print_token(multi_parser.token_text);
558 |             multi_parser.current_mode = 'CONTENT';
559 |             break;
560 |           case 'TK_CONTENT':
561 |             if (multi_parser.token_text !== '') {
562 |               multi_parser.print_token(multi_parser.token_text);
563 |             }
564 |             multi_parser.current_mode = 'TAG';
565 |             break;
566 |           case 'TK_STYLE':
567 |           case 'TK_SCRIPT':
568 |             if (multi_parser.token_text !== '') {
569 |               multi_parser.output.push('\n');
570 |               var text = multi_parser.token_text,
571 |                   _beautifier,
572 |                   script_indent_level = 1;
573 |               if (multi_parser.token_type === 'TK_SCRIPT') {
574 |                 _beautifier = typeof js_beautify === 'function' && js_beautify;
575 |               } else if (multi_parser.token_type === 'TK_STYLE') {
576 |                 _beautifier = typeof css_beautify === 'function' && css_beautify;
577 |               }
578 | 
579 |               if (options.indent_scripts === "keep") {
580 |                 script_indent_level = 0;
581 |               } else if (options.indent_scripts === "separate") {
582 |                 script_indent_level = -multi_parser.indent_level;
583 |               }
584 | 
585 |               var indentation = multi_parser.get_full_indent(script_indent_level);
586 |               if (_beautifier) {
587 |                 // call the Beautifier if avaliable
588 |                 text = _beautifier(text.replace(/^\s*/, indentation), options);
589 |               } else {
590 |                 // simply indent the string otherwise
591 |                 var white = text.match(/^\s*/)[0];
592 |                 var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
593 |                 var reindent = multi_parser.get_full_indent(script_indent_level -_level);
594 |                 text = text.replace(/^\s*/, indentation)
595 |                        .replace(/\r\n|\r|\n/g, '\n' + reindent)
596 |                        .replace(/\s*$/, '');
597 |               }
598 |               if (text) {
599 |                 multi_parser.print_token(text);
600 |                 multi_parser.print_newline(true, multi_parser.output);
601 |               }
602 |             }
603 |             multi_parser.current_mode = 'TAG';
604 |             break;
605 |         }
606 |         multi_parser.last_token = multi_parser.token_type;
607 |         multi_parser.last_text = multi_parser.token_text;
608 |       }
609 |       return multi_parser.output.join('');
610 |     }
611 | 
612 |     // If we're running a web page and don't have either of the above, add our one global
613 |     window.html_beautify = function(html_source, options) {
614 |         return style_html(html_source, options, window.js_beautify, window.css_beautify);
615 |     };
616 | 
617 | }());


--------------------------------------------------------------------------------
/js/bootstrap-editor.js:
--------------------------------------------------------------------------------
  1 | // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
  2 | // MIT License. See license.txt 
  3 | 
  4 | /* Inspired from: http://github.com/mindmup/bootstrap-wysiwyg */
  5 | 
  6 | // todo 
  7 | // make it inline friendly
  8 | 
  9 | wn.provide("wn.ui");
 10 | wn.ui.Editor = Class.extend({
 11 | 	init: function(options) {
 12 | 		this.options = $.extend(options || {}, this.default_options);
 13 | 		if(this.options.editor) {
 14 | 			this.setup_editor(this.options.editor);
 15 | 			this.setup_fixed_toolbar();
 16 | 		} else if(this.options.parent) {
 17 | 			this.wrapper = $("
").appendTo(this.options.parent); 18 | this.setup_editor($("
").appendTo(this.wrapper)); 19 | this.setup_inline_toolbar(); 20 | this.editor.css(this.options.inline_editor_style); 21 | this.set_editing(); 22 | } 23 | }, 24 | setup_editor: function(editor) { 25 | var me = this; 26 | this.editor = $(editor); 27 | this.editor.on("click", function() { 28 | if(!this.editing) { 29 | me.set_editing(); 30 | } 31 | }).on("mouseup keyup mouseout", function() { 32 | if(me.editing) { 33 | me.toolbar.save_selection(); 34 | me.toolbar.update(); 35 | me.options.change && me.options.change(me.clean_html()); 36 | } 37 | }).data("object", this); 38 | 39 | this.bind_hotkeys(); 40 | this.init_file_drops(); 41 | 42 | }, 43 | 44 | set_editing: function() { 45 | this.editor.attr('contenteditable', true); 46 | this.original_html = this.editor.html(); 47 | this.toolbar.show(); 48 | this.toolbar.editor = this.editor.focus(); 49 | this.editing = true; 50 | }, 51 | 52 | setup_fixed_toolbar: function() { 53 | if(!wn._editor_toolbar) { 54 | wn._editor_toolbar = new wn.ui.EditorToolbar(this.options) 55 | } 56 | this.toolbar = wn._editor_toolbar; 57 | }, 58 | setup_inline_toolbar: function() { 59 | this.toolbar = new wn.ui.EditorToolbar(this.options, this.wrapper); 60 | }, 61 | onhide: function(action) { 62 | this.editing = false; 63 | if(action==="Cancel") { 64 | this.editor.html(this.original_html); 65 | this.options.oncancel && this.options.oncancel(this); 66 | } else { 67 | this.options.onsave && this.options.onsave(this); 68 | this.options.change && this.options.change(this.get_value()); 69 | } 70 | }, 71 | default_options: { 72 | hotKeys: { 73 | 'ctrl+b meta+b': 'bold', 74 | 'ctrl+i meta+i': 'italic', 75 | 'ctrl+u meta+u': 'underline', 76 | 'ctrl+z meta+z': 'undo', 77 | 'ctrl+y meta+y meta+shift+z': 'redo', 78 | 'ctrl+l meta+l': 'justifyleft', 79 | 'ctrl+e meta+e': 'justifycenter', 80 | 'ctrl+j meta+j': 'justifyfull', 81 | 'shift+tab': 'outdent', 82 | 'tab': 'indent' 83 | }, 84 | inline_editor_style: { 85 | "height": "400px", 86 | "background-color": "white", 87 | "border-collapse": "separate", 88 | "border": "1px solid rgb(204, 204, 204)", 89 | "padding": "4px", 90 | "box-sizing": "content-box", 91 | "-webkit-box-shadow": "rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset", 92 | "box-shadow": "rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset", 93 | "border-radius": "3px", 94 | "overflow": "scroll", 95 | "outline": "none" 96 | }, 97 | toolbar_selector: '[data-role=editor-toolbar]', 98 | command_role: 'edit', 99 | active_toolbar_class: 'btn-info', 100 | selection_marker: 'edit-focus-marker', 101 | selection_color: 'darkgrey', 102 | remove_typography: true, 103 | max_file_size: 1, 104 | }, 105 | 106 | bind_hotkeys: function () { 107 | var me = this; 108 | $.each(this.options.hotKeys, function (hotkey, command) { 109 | me.editor.keydown(hotkey, function (e) { 110 | if (me.editor.attr('contenteditable') && me.editor.is(':visible')) { 111 | e.preventDefault(); 112 | e.stopPropagation(); 113 | me.toolbar.execCommand(command); 114 | } 115 | }).keyup(hotkey, function (e) { 116 | if (me.editor.attr('contenteditable') && me.editor.is(':visible')) { 117 | e.preventDefault(); 118 | e.stopPropagation(); 119 | } 120 | }); 121 | }); 122 | }, 123 | 124 | clean_html: function() { 125 | var html = this.editor.html() || ""; 126 | html = html.replace(/(
|\s|

<\/div>| )*$/, ''); 127 | 128 | // remove custom typography (use CSS!) 129 | if(this.options.remove_typography) { 130 | html = html.replace(/(font-family|font-size|line-height):[^;]*;/g, ''); 131 | html = html.replace(/<[^>]*(font=['"][^'"]*['"])>/g, function(a,b) { return a.replace(b, ''); }); 132 | html = html.replace(/\s*style\s*=\s*["']\s*["']/g, ''); 133 | return html; 134 | } 135 | }, 136 | 137 | init_file_drops: function () { 138 | var me = this; 139 | this.editor.on('dragenter dragover', false) 140 | .on('drop', function (e) { 141 | var dataTransfer = e.originalEvent.dataTransfer; 142 | e.stopPropagation(); 143 | e.preventDefault(); 144 | if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { 145 | me.insert_files(dataTransfer.files); 146 | } 147 | }); 148 | }, 149 | 150 | insert_files: function (files) { 151 | var me = this; 152 | this.editor.focus(); 153 | $.each(files, function (i, file) { 154 | if (/^image\//.test(file.type)) { 155 | me.get_image(file, function(image_url) { 156 | me.toolbar.execCommand('insertimage', image_url); 157 | }) 158 | } 159 | }); 160 | }, 161 | 162 | get_image: function (fileobj, callback) { 163 | var freader = new FileReader(), 164 | me = this; 165 | 166 | freader.onload = function() { 167 | var dataurl = freader.result; 168 | // add filename to dataurl 169 | var parts = dataurl.split(","); 170 | parts[0] += ";filename=" + fileobj.name; 171 | dataurl = parts[0] + ',' + parts[1]; 172 | if(me.options.max_file_size) { 173 | if(dataurl.length > (me.options.max_file_size * 1024 * 1024 * 1.4)) { 174 | wn.msgprint("Max file size (" + me.options.max_file_size + "M) exceeded."); 175 | throw "file size exceeded"; 176 | } 177 | } 178 | callback(dataurl); 179 | } 180 | freader.readAsDataURL(fileobj); 181 | }, 182 | 183 | get_value: function() { 184 | return this.clean_html() 185 | }, 186 | 187 | set_input: function(value) { 188 | if(this.options.field.inside_change_event) 189 | return; 190 | this.editor.html(value==null ? "" : value); 191 | } 192 | 193 | }) 194 | 195 | wn.ui.EditorToolbar = Class.extend({ 196 | init: function(options, parent) { 197 | this.options = options; 198 | this.inline = !!parent; 199 | this.options.toolbar_style = $.extend(this.options.toolbar_style || {}, 200 | (this.inline ? this.inline_style : this.fixed_style)); 201 | this.make(parent); 202 | this.toolbar.css(this.options.toolbar_style); 203 | this.setup_image_button(); 204 | this.bind_events(); 205 | this.bind_touch(); 206 | 207 | var me = this; 208 | $(document).mousedown(function(e) { 209 | me.clicked = $(e.target); 210 | }); 211 | }, 212 | fixed_style: { 213 | position: "fixed", 214 | top: "0px", 215 | padding: "5px", 216 | width: "100%", 217 | height: "45px", 218 | "background-color": "#777" 219 | }, 220 | inline_style: { 221 | padding: "5px", 222 | }, 223 | make: function(parent) { 224 | if(!parent) 225 | parent = $("body"); 226 | if(!parent.find(".wn-editor-toolbar").length) { 227 | this.toolbar = $('
\ 228 |
\ 229 |
\ 230 | \ 232 | \ 240 |
\ 241 |
\ 242 | \ 243 | \ 244 | \ 245 | \ 246 | \ 247 | \ 248 | \ 249 | \ 250 | \ 251 | \ 252 |
\ 253 | \ 267 |
\ 268 | \ 269 | \ 270 | \ 271 | \ 272 | \ 273 | \ 274 |
\ 275 | \ 276 |
').prependTo(parent); 277 | 278 | if(this.inline) { 279 | this.toolbar.find("[data-action]").remove(); 280 | } else { 281 | this.toolbar.find(".btn-toolbar").addClass("container"); 282 | } 283 | } 284 | }, 285 | 286 | setup_image_button: function() { 287 | // magic-overlay 288 | var me = this; 289 | this.file_input = this.toolbar.find('input[type="file"]') 290 | .css({ 291 | 'opacity':0, 292 | 'position':'absolute', 293 | 'left':0, 294 | 'width':0, 295 | 'height':0 296 | }); 297 | this.toolbar.find(".btn-insert-img").on("click", function() { 298 | me.file_input.trigger("click"); 299 | }) 300 | }, 301 | 302 | show: function() { 303 | var me = this; 304 | this.toolbar.toggle(true); 305 | $("body").animate({"padding-top": this.toolbar.outerHeight() }, { 306 | complete: function() { me.toolbar.css("z-index", 1001); } 307 | }); 308 | }, 309 | 310 | hide: function(action) { 311 | var me = this; 312 | this.toolbar.css("z-index", 0); 313 | $("body").animate({"padding-top": 0 }, {complete: function() { 314 | me.toolbar.toggle(false); 315 | }}); 316 | 317 | this.editor && this.editor.attr('contenteditable', false).data("object").onhide(action); 318 | this.editor = null; 319 | }, 320 | 321 | bind_events: function () { 322 | var me = this; 323 | 324 | // standard button events 325 | this.toolbar.find('a[data-' + me.options.command_role + ']').click(function () { 326 | me.restore_selection(); 327 | me.editor.focus(); 328 | me.execCommand($(this).data(me.options.command_role)); 329 | me.save_selection(); 330 | return false; 331 | }); 332 | this.toolbar.find('[data-toggle=dropdown]').click(function() { me.restore_selection() }); 333 | 334 | // link 335 | this.toolbar.find(".btn-add-link").on("click", function() { 336 | if(!wn._link_editor) { 337 | wn._link_editor = new wn.ui.LinkEditor(me); 338 | } 339 | wn._link_editor.show(); 340 | }) 341 | 342 | // file event 343 | this.toolbar.find('input[type=file][data-' + me.options.command_role + ']').change(function () { 344 | me.restore_selection(); 345 | if (this.type === 'file' && this.files && this.files.length > 0) { 346 | me.editor.data("object").insert_files(this.files); 347 | } 348 | me.save_selection(); 349 | this.value = ''; 350 | return false; 351 | }); 352 | 353 | // save 354 | this.toolbar.find("[data-action='Save']").on("click", function() { 355 | me.hide("Save"); 356 | }) 357 | 358 | // cancel 359 | this.toolbar.find("[data-action='Cancel']").on("click", function() { 360 | me.hide("Cancel"); 361 | }) 362 | 363 | // edit html 364 | this.toolbar.find(".btn-html").on("click", function() { 365 | if(!wn._html_editor) 366 | wn._html_editor = new wn.ui.HTMLEditor(); 367 | 368 | wn._html_editor.show(me.editor); 369 | }) 370 | }, 371 | 372 | update: function () { 373 | var me = this; 374 | if (this.options.active_toolbar_class) { 375 | $(this.options.toolbar_selector).find('.btn[data-' + this.options.command_role + ']').each(function () { 376 | var command = $(this).data(me.options.command_role); 377 | if (document.queryCommandState(command)) { 378 | $(this).addClass(me.options.active_toolbar_class); 379 | } else { 380 | $(this).removeClass(me.options.active_toolbar_class); 381 | } 382 | }); 383 | } 384 | }, 385 | 386 | execCommand: function (commandWithArgs, valueArg) { 387 | var commandArr = commandWithArgs.split(' '), 388 | command = commandArr.shift(), 389 | args = commandArr.join(' ') + (valueArg || ''); 390 | document.execCommand(command, 0, args); 391 | this.update(); 392 | }, 393 | 394 | get_current_range: function () { 395 | var sel = window.getSelection(); 396 | if (sel.getRangeAt && sel.rangeCount) { 397 | return sel.getRangeAt(0); 398 | } 399 | }, 400 | 401 | save_selection: function () { 402 | this.selected_range = this.get_current_range(); 403 | this.selected_html = this.get_current_html(); 404 | }, 405 | 406 | get_current_html: function() { 407 | var html = ""; 408 | if (typeof window.getSelection != "undefined") { 409 | var sel = window.getSelection(); 410 | if (sel.rangeCount) { 411 | var container = document.createElement("div"); 412 | for (var i = 0, len = sel.rangeCount; i < len; ++i) { 413 | container.appendChild(sel.getRangeAt(i).cloneContents()); 414 | } 415 | html = container.innerHTML; 416 | } 417 | } else if (typeof document.selection != "undefined") { 418 | if (document.selection.type == "Text") { 419 | html = document.selection.createRange().htmlText; 420 | } 421 | } 422 | return html; 423 | }, 424 | 425 | restore_selection: function () { 426 | var selection = window.getSelection(); 427 | if (this.selected_range) { 428 | selection.removeAllRanges(); 429 | selection.addRange(this.selected_range); 430 | } 431 | }, 432 | 433 | mark_selection: function (input, color) { 434 | this.restore_selection(); 435 | document.execCommand('hiliteColor', 0, color || 'transparent'); 436 | this.save_selection(); 437 | input.data(this.options.selection_marker, color); 438 | }, 439 | 440 | bind_touch: function() { 441 | var me = this; 442 | $(window).bind('touchend', function (e) { 443 | var isInside = (me.editor.is(e.target) || me.editor.has(e.target).length > 0), 444 | current_range = me.get_current_range(), 445 | clear = current_range && (current_range.startContainer === current_range.endContainer && current_range.startOffset === current_range.endOffset); 446 | if (!clear || isInside) { 447 | me.save_selection(); 448 | me.update(); 449 | } 450 | }); 451 | } 452 | }); 453 | 454 | wn.ui.HTMLEditor = Class.extend({ 455 | init: function() { 456 | var me = this; 457 | this.modal = wn.get_modal("Edit HTML", '
\ 460 | '); 461 | this.modal.addClass("wn-ignore-click"); 462 | this.modal.find(".btn-primary").on("click", function() { 463 | me.editor.html(me.modal.find("textarea").val()); 464 | me.modal.modal("hide"); 465 | }); 466 | }, 467 | show: function(editor) { 468 | var me = this; 469 | this.editor = editor; 470 | this.modal.modal("show") 471 | this.modal.find("textarea").html(html_beautify(me.editor.html())); 472 | } 473 | }); 474 | 475 | wn.ui.LinkEditor = Class.extend({ 476 | init: function(toolbar) { 477 | var me = this; 478 | this.toolbar = toolbar; 479 | this.modal = wn.get_modal("Edit HTML", '
\ 480 | \ 481 |
\ 482 |
\ 483 | \ 486 |
\ 487 | '); 488 | 489 | this.modal.addClass("wn-ignore-click"); 490 | this.modal.find(".btn-primary").on("click", function() { 491 | me.toolbar.restore_selection(); 492 | var url = me.modal.find("input[type=text]").val(); 493 | var selection = me.toolbar.selected_range.toString(); 494 | if(url) { 495 | if(me.modal.find("input[type=checkbox]:checked").length) { 496 | var html = "" + selection + ""; 497 | document.execCommand("insertHTML", false, html); 498 | } else { 499 | document.execCommand("CreateLink", false, url); 500 | } 501 | } 502 | me.modal.modal("hide"); 503 | return false; 504 | }); 505 | }, 506 | show: function() { 507 | this.modal.find("input[type=text]").val(""); 508 | this.modal.modal("show"); 509 | } 510 | }) -------------------------------------------------------------------------------- /js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap.js v3.0.0 by @fat and @mdo 3 | * Copyright 2013 Twitter Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]');if(a.length){var b=this.$element.find("input").prop("checked",!this.$element.hasClass("active")).trigger("change");"radio"===b.prop("type")&&a.find(".active").removeClass("active")}this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(window.jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery); -------------------------------------------------------------------------------- /js/class.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | see: http://ejohn.org/blog/simple-javascript-inheritance/ 4 | To subclass, use: 5 | 6 | var MyClass = Class.extend({ 7 | init: function 8 | }) 9 | 10 | */ 11 | 12 | /* Simple JavaScript Inheritance 13 | * By John Resig http://ejohn.org/ 14 | * MIT Licensed. 15 | */ 16 | // Inspired by base2 and Prototype 17 | 18 | (function(){ 19 | var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; 20 | // The base Class implementation (does nothing) 21 | this.Class = function(){}; 22 | 23 | // Create a new Class that inherits from this class 24 | Class.extend = function(prop) { 25 | var _super = this.prototype; 26 | 27 | // Instantiate a base class (but only create the instance, 28 | // don't run the init constructor) 29 | initializing = true; 30 | var prototype = new this(); 31 | initializing = false; 32 | 33 | // Copy the properties over onto the new prototype 34 | for (var name in prop) { 35 | // Check if we're overwriting an existing function 36 | prototype[name] = typeof prop[name] == "function" && 37 | typeof _super[name] == "function" && fnTest.test(prop[name]) ? 38 | (function(name, fn){ 39 | return function() { 40 | var tmp = this._super; 41 | 42 | // Add a new ._super() method that is the same method 43 | // but on the super-class 44 | this._super = _super[name]; 45 | 46 | // The method only need to be bound temporarily, so we 47 | // remove it when we're done executing 48 | var ret = fn.apply(this, arguments); 49 | this._super = tmp; 50 | 51 | return ret; 52 | }; 53 | })(name, prop[name]) : 54 | prop[name]; 55 | } 56 | 57 | // The dummy class constructor 58 | function Class() { 59 | // All construction is actually done in the init method 60 | this._type = "instance"; 61 | if ( !initializing && this.init ) 62 | this.init.apply(this, arguments); 63 | } 64 | 65 | // Populate our constructed prototype object 66 | Class.prototype = prototype; 67 | Class._type = "class"; 68 | 69 | // Enforce the constructor to be what we expect 70 | Class.prototype.constructor = Class; 71 | 72 | // And make this class extendable 73 | Class.extend = arguments.callee; 74 | 75 | return Class; 76 | }; 77 | })(); 78 | -------------------------------------------------------------------------------- /js/editor.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. 2 | // MIT License. See license.txt 3 | 4 | /* Inspired from: http://github.com/mindmup/bootstrap-wysiwyg */ 5 | 6 | // todo 7 | // make it inline friendly 8 | 9 | bsEditor = Class.extend({ 10 | init: function(options) { 11 | this.options = $.extend(options || {}, this.default_options); 12 | if(this.options.editor) { 13 | this.setup_editor(this.options.editor); 14 | this.setup_fixed_toolbar(); 15 | } else if(this.options.parent) { 16 | this.wrapper = $("
").appendTo(this.options.parent); 17 | this.setup_editor($("
").appendTo(this.wrapper)); 18 | this.setup_inline_toolbar(); 19 | this.editor.css(this.options.inline_editor_style); 20 | this.set_editing(); 21 | } 22 | }, 23 | setup_editor: function(editor) { 24 | var me = this; 25 | this.editor = $(editor); 26 | this.editor.on("click", function() { 27 | if(!me.editing) { 28 | me.set_editing(); 29 | } 30 | }).on("mouseup keyup mouseout", function() { 31 | if(me.editing) { 32 | me.toolbar.save_selection(); 33 | me.toolbar.update(); 34 | me.options.change && me.options.change(me.clean_html()); 35 | } 36 | }).data("object", this); 37 | 38 | this.bind_hotkeys(); 39 | this.init_file_drops(); 40 | 41 | }, 42 | 43 | set_editing: function() { 44 | this.editor.attr('contenteditable', true); 45 | this.original_html = this.editor.html(); 46 | this.toolbar.show(); 47 | this.toolbar.editor = this.editor.focus(); 48 | this.editing = true; 49 | }, 50 | 51 | setup_fixed_toolbar: function() { 52 | if(!window.bs_editor_toolbar) { 53 | window.bs_editor_toolbar = new bsEditorToolbar(this.options) 54 | } 55 | this.toolbar = window.bs_editor_toolbar; 56 | }, 57 | setup_inline_toolbar: function() { 58 | this.toolbar = new bsEditorToolbar(this.options, this.wrapper); 59 | }, 60 | onhide: function(action) { 61 | this.editing = false; 62 | if(action==="Cancel") { 63 | this.editor.html(this.original_html); 64 | this.options.oncancel && this.options.oncancel(this); 65 | } else { 66 | this.options.onsave && this.options.onsave(this); 67 | this.options.change && this.options.change(this.get_value()); 68 | } 69 | }, 70 | default_options: { 71 | hotKeys: { 72 | 'ctrl+b meta+b': 'bold', 73 | 'ctrl+i meta+i': 'italic', 74 | 'ctrl+u meta+u': 'underline', 75 | 'ctrl+z meta+z': 'undo', 76 | 'ctrl+y meta+y meta+shift+z': 'redo', 77 | 'ctrl+l meta+l': 'justifyleft', 78 | 'ctrl+e meta+e': 'justifycenter', 79 | 'ctrl+j meta+j': 'justifyfull', 80 | 'shift+tab': 'outdent', 81 | 'tab': 'indent' 82 | }, 83 | inline_editor_style: { 84 | "height": "400px", 85 | "background-color": "white", 86 | "border-collapse": "separate", 87 | "border": "1px solid rgb(204, 204, 204)", 88 | "padding": "4px", 89 | "box-sizing": "content-box", 90 | "-webkit-box-shadow": "rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset", 91 | "box-shadow": "rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset", 92 | "border-radius": "3px", 93 | "overflow": "scroll", 94 | "outline": "none" 95 | }, 96 | toolbar_selector: '[data-role=editor-toolbar]', 97 | command_role: 'edit', 98 | active_toolbar_class: 'btn-info', 99 | selection_marker: 'edit-focus-marker', 100 | selection_color: 'darkgrey', 101 | remove_typography: true, 102 | max_file_size: 1, 103 | }, 104 | 105 | bind_hotkeys: function () { 106 | var me = this; 107 | $.each(this.options.hotKeys, function (hotkey, command) { 108 | me.editor.keydown(hotkey, function (e) { 109 | if (me.editor.attr('contenteditable') && me.editor.is(':visible')) { 110 | e.preventDefault(); 111 | e.stopPropagation(); 112 | me.toolbar.execCommand(command); 113 | } 114 | }).keyup(hotkey, function (e) { 115 | if (me.editor.attr('contenteditable') && me.editor.is(':visible')) { 116 | e.preventDefault(); 117 | e.stopPropagation(); 118 | } 119 | }); 120 | }); 121 | }, 122 | 123 | clean_html: function() { 124 | var html = this.editor.html() || ""; 125 | html = html.replace(/(
|\s|

<\/div>| )*$/, ''); 126 | 127 | // remove custom typography (use CSS!) 128 | if(this.options.remove_typography) { 129 | html = html.replace(/(font-family|font-size|line-height):[^;]*;/g, ''); 130 | html = html.replace(/<[^>]*(font=['"][^'"]*['"])>/g, function(a,b) { return a.replace(b, ''); }); 131 | html = html.replace(/\s*style\s*=\s*["']\s*["']/g, ''); 132 | return html; 133 | } 134 | }, 135 | 136 | init_file_drops: function () { 137 | var me = this; 138 | this.editor.on('dragenter dragover', false) 139 | .on('drop', function (e) { 140 | var dataTransfer = e.originalEvent.dataTransfer; 141 | e.stopPropagation(); 142 | e.preventDefault(); 143 | if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { 144 | me.insert_files(dataTransfer.files); 145 | } 146 | }); 147 | }, 148 | 149 | insert_files: function (files) { 150 | var me = this; 151 | this.editor.focus(); 152 | $.each(files, function (i, file) { 153 | if (/^image\//.test(file.type)) { 154 | me.get_image(file, function(image_url) { 155 | me.toolbar.execCommand('insertimage', image_url); 156 | }) 157 | } 158 | }); 159 | }, 160 | 161 | get_image: function (fileobj, callback) { 162 | var freader = new FileReader(), 163 | me = this; 164 | 165 | freader.onload = function() { 166 | var dataurl = freader.result; 167 | // add filename to dataurl 168 | var parts = dataurl.split(","); 169 | parts[0] += ";filename=" + fileobj.name; 170 | dataurl = parts[0] + ',' + parts[1]; 171 | if(me.options.max_file_size) { 172 | if(dataurl.length > (me.options.max_file_size * 1024 * 1024 * 1.4)) { 173 | bs_get_modal("Upload Error", "Max file size (" + me.options.max_file_size + "M) exceeded.").modal("show"); 174 | throw "file size exceeded"; 175 | } 176 | } 177 | callback(dataurl); 178 | } 179 | freader.readAsDataURL(fileobj); 180 | }, 181 | 182 | get_value: function() { 183 | return this.clean_html() 184 | }, 185 | 186 | set_input: function(value) { 187 | if(this.options.field && this.options.field.inside_change_event) 188 | return; 189 | this.editor.html(value==null ? "" : value); 190 | } 191 | 192 | }) 193 | 194 | bsEditorToolbar = Class.extend({ 195 | init: function(options, parent) { 196 | this.options = options; 197 | this.inline = !!parent; 198 | this.options.toolbar_style = $.extend((this.inline ? this.inline_style : this.fixed_style), 199 | this.options.toolbar_style || {}); 200 | this.make(parent); 201 | this.toolbar.css(this.options.toolbar_style); 202 | this.setup_image_button(); 203 | this.bind_events(); 204 | //this.bind_touch(); 205 | }, 206 | fixed_style: { 207 | position: "fixed", 208 | top: "0px", 209 | padding: "5px", 210 | width: "100%", 211 | height: "45px", 212 | "background-color": "#777", 213 | display: "none" 214 | }, 215 | inline_style: { 216 | padding: "5px", 217 | }, 218 | make: function(parent) { 219 | if(!parent) 220 | parent = $("body"); 221 | if(!parent.find(".wn-editor-toolbar").length) { 222 | this.toolbar = $('
\ 223 |
\ 224 |
\ 225 | \ 227 | \ 235 |
\ 236 |
\ 237 | \ 238 | \ 239 | \ 240 | \ 241 | \ 242 | \ 243 | \ 244 | \ 245 | \ 246 | \ 247 |
\ 248 | \ 262 |
\ 263 | \ 264 | \ 265 | \ 266 | \ 267 | \ 268 | \ 269 |
\ 270 | \ 271 |
').prependTo(parent); 272 | 273 | if(this.inline) { 274 | this.toolbar.find("[data-action]").remove(); 275 | } else { 276 | this.toolbar.find(".btn-toolbar").addClass("container"); 277 | } 278 | } 279 | }, 280 | 281 | setup_image_button: function() { 282 | // magic-overlay 283 | var me = this; 284 | this.file_input = this.toolbar.find('input[type="file"]') 285 | .css({ 286 | 'opacity':0, 287 | 'position':'absolute', 288 | 'left':0, 289 | 'width':0, 290 | 'height':0 291 | }); 292 | this.toolbar.find(".btn-insert-img").on("click", function() { 293 | me.file_input.trigger("click"); 294 | }) 295 | }, 296 | 297 | show: function() { 298 | var me = this; 299 | this.toolbar.toggle(true); 300 | $("body").animate({"padding-top": this.toolbar.outerHeight() }, { 301 | complete: function() { me.toolbar.css("z-index", 1001); } 302 | }); 303 | }, 304 | 305 | hide: function(action) { 306 | if(!this.editor) 307 | return; 308 | var me = this; 309 | this.toolbar.css("z-index", 0); 310 | $("body").animate({"padding-top": 0 }, {complete: function() { 311 | me.toolbar.toggle(false); 312 | }}); 313 | 314 | this.editor && this.editor.attr('contenteditable', false).data("object").onhide(action); 315 | this.editor = null; 316 | }, 317 | 318 | bind_events: function () { 319 | var me = this; 320 | 321 | // standard button events 322 | this.toolbar.find('a[data-' + me.options.command_role + ']').click(function () { 323 | me.restore_selection(); 324 | me.editor.focus(); 325 | me.execCommand($(this).data(me.options.command_role)); 326 | me.save_selection(); 327 | // close dropdown 328 | me.toolbar.find('[data-toggle=dropdown]').dropdown("hide"); 329 | return false; 330 | }); 331 | this.toolbar.find('[data-toggle=dropdown]').click(function() { me.restore_selection() }); 332 | 333 | // link 334 | this.toolbar.find(".btn-add-link").on("click", function() { 335 | if(!window.bs_link_editor) { 336 | window.bs_link_editor = new bsLinkEditor(me); 337 | } 338 | window.bs_link_editor.show(); 339 | }) 340 | 341 | // file event 342 | this.toolbar.find('input[type=file][data-' + me.options.command_role + ']').change(function () { 343 | me.restore_selection(); 344 | if (this.type === 'file' && this.files && this.files.length > 0) { 345 | me.editor.data("object").insert_files(this.files); 346 | } 347 | me.save_selection(); 348 | this.value = ''; 349 | return false; 350 | }); 351 | 352 | // save 353 | this.toolbar.find("[data-action='Save']").on("click", function() { 354 | me.hide("Save"); 355 | }) 356 | 357 | // cancel 358 | this.toolbar.find("[data-action='Cancel']").on("click", function() { 359 | me.hide("Cancel"); 360 | }) 361 | 362 | // edit html 363 | this.toolbar.find(".btn-html").on("click", function() { 364 | if(!window.bs_html_editor) 365 | window.bs_html_editor = new bsHTMLEditor(); 366 | 367 | window.bs_html_editor.show(me.editor); 368 | }) 369 | }, 370 | 371 | update: function () { 372 | var me = this; 373 | if (this.toolbar) { 374 | $(this.toolbar).find('.btn[data-' + this.options.command_role + ']').each(function () { 375 | var command = $(this).data(me.options.command_role); 376 | if (document.queryCommandState(command)) { 377 | $(this).addClass(me.options.active_toolbar_class); 378 | } else { 379 | $(this).removeClass(me.options.active_toolbar_class); 380 | } 381 | }); 382 | } 383 | }, 384 | 385 | execCommand: function (commandWithArgs, valueArg) { 386 | var commandArr = commandWithArgs.split(' '), 387 | command = commandArr.shift(), 388 | args = commandArr.join(' ') + (valueArg || ''); 389 | document.execCommand(command, 0, args); 390 | this.update(); 391 | }, 392 | 393 | get_current_range: function () { 394 | var sel = window.getSelection(); 395 | if (sel.getRangeAt && sel.rangeCount) { 396 | return sel.getRangeAt(0); 397 | } 398 | }, 399 | 400 | save_selection: function () { 401 | this.selected_range = this.get_current_range(); 402 | }, 403 | 404 | restore_selection: function () { 405 | var selection = window.getSelection(); 406 | if (this.selected_range) { 407 | selection.removeAllRanges(); 408 | selection.addRange(this.selected_range); 409 | } 410 | }, 411 | 412 | mark_selection: function (input, color) { 413 | this.restore_selection(); 414 | document.execCommand('hiliteColor', 0, color || 'transparent'); 415 | this.save_selection(); 416 | input.data(this.options.selection_marker, color); 417 | }, 418 | 419 | // bind_touch: function() { 420 | // var me = this; 421 | // $(window).bind('touchend', function (e) { 422 | // var isInside = (me.editor.is(e.target) || me.editor.has(e.target).length > 0), 423 | // current_range = me.get_current_range(), 424 | // clear = current_range && (current_range.startContainer === current_range.endContainer && current_range.startOffset === current_range.endOffset); 425 | // if (!clear || isInside) { 426 | // me.save_selection(); 427 | // me.update(); 428 | // } 429 | // }); 430 | // } 431 | }); 432 | 433 | bsHTMLEditor = Class.extend({ 434 | init: function() { 435 | var me = this; 436 | this.modal = bs_get_modal(" Edit HTML", '
\ 439 | '); 440 | this.modal.addClass("wn-ignore-click"); 441 | this.modal.find(".btn-primary").on("click", function() { 442 | var html = me.modal.find("textarea").val(); 443 | $.each(me.dataurls, function(key, val) { 444 | html = html.replace(key, val); 445 | }) 446 | me.editor.html(); 447 | me.modal.modal("hide"); 448 | }); 449 | }, 450 | show: function(editor) { 451 | var me = this; 452 | this.editor = editor; 453 | this.modal.modal("show") 454 | var html = me.editor.html(); 455 | // pack dataurls so that html display is faster 456 | this.dataurls = {} 457 | html = html.replace(/ Insert Link", '
\ 471 | \ 472 |
\ 473 |
\ 474 | \ 477 |
\ 478 | '); 479 | 480 | this.modal.addClass("wn-ignore-click"); 481 | this.modal.find(".btn-primary").on("click", function() { 482 | me.toolbar.restore_selection(); 483 | var url = me.modal.find("input[type=text]").val(); 484 | var selection = me.toolbar.selected_range.toString(); 485 | if(url) { 486 | if(me.modal.find("input[type=checkbox]:checked").length) { 487 | var html = "" + selection + ""; 488 | document.execCommand("insertHTML", false, html); 489 | } else { 490 | document.execCommand("CreateLink", false, url); 491 | } 492 | } 493 | me.modal.modal("hide"); 494 | return false; 495 | }); 496 | }, 497 | show: function() { 498 | this.modal.find("input[type=text]").val(""); 499 | this.modal.modal("show"); 500 | } 501 | }); 502 | 503 | bs_get_modal = function(title, body_html) { 504 | var modal = $('').appendTo(document.body); 517 | 518 | return modal; 519 | }; 520 | -------------------------------------------------------------------------------- /js/highlight.pack.js: -------------------------------------------------------------------------------- 1 | var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(//gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"
")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.javascript=function(a){return{k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const",literal:"true false null undefined NaN Infinity"},c:[a.ASM,a.QSM,a.CLCM,a.CBLCLM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",i:"\\n",c:[{b:"\\\\/"}]},{b:"<",e:">;",sL:"xml"}],r:0},{cN:"function",bWK:true,e:"{",k:"function",c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[a.CLCM,a.CBLCLM],i:"[\"'\\(]"}],i:"\\[|%"}]}}(hljs); -------------------------------------------------------------------------------- /js/jquery.hotkeys.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Hotkeys Plugin 3 | * Copyright 2010, John Resig 4 | * Dual licensed under the MIT or GPL Version 2 licenses. 5 | * 6 | * Based upon the plugin by Tzury Bar Yochay: 7 | * http://github.com/tzuryby/hotkeys 8 | * 9 | * Original idea by: 10 | * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ 11 | */ 12 | 13 | (function(jQuery){ 14 | 15 | jQuery.hotkeys = { 16 | version: "0.8", 17 | 18 | specialKeys: { 19 | 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause", 20 | 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home", 21 | 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del", 22 | 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", 23 | 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/", 24 | 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8", 25 | 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta" 26 | }, 27 | 28 | shiftNums: { 29 | "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", 30 | "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<", 31 | ".": ">", "/": "?", "\\": "|" 32 | } 33 | }; 34 | 35 | function keyHandler( handleObj ) { 36 | // Only care when a possible input has been specified 37 | if ( typeof handleObj.data !== "string" ) { 38 | return; 39 | } 40 | 41 | var origHandler = handleObj.handler, 42 | keys = handleObj.data.toLowerCase().split(" "), 43 | textAcceptingInputTypes = ["text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", "datetime-local", "search", "color"]; 44 | 45 | handleObj.handler = function( event ) { 46 | // Don't fire in text-accepting inputs that we didn't directly bind to 47 | // if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) || 48 | // jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1 ) ) { 49 | // return; 50 | // } 51 | 52 | // Keypress represents characters, not special keys 53 | var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ], 54 | character = String.fromCharCode( event.which ).toLowerCase(), 55 | key, modif = "", possible = {}; 56 | 57 | // check combinations (alt|ctrl|shift+anything) 58 | if ( event.altKey && special !== "alt" ) { 59 | modif += "alt+"; 60 | } 61 | 62 | if ( event.ctrlKey && special !== "ctrl" ) { 63 | modif += "ctrl+"; 64 | } 65 | 66 | // TODO: Need to make sure this works consistently across platforms 67 | if ( event.metaKey && !event.ctrlKey && special !== "meta" ) { 68 | modif += "meta+"; 69 | } 70 | 71 | if ( event.shiftKey && special !== "shift" ) { 72 | modif += "shift+"; 73 | } 74 | 75 | if ( special ) { 76 | possible[ modif + special ] = true; 77 | 78 | } else { 79 | possible[ modif + character ] = true; 80 | possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true; 81 | 82 | // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" 83 | if ( modif === "shift+" ) { 84 | possible[ jQuery.hotkeys.shiftNums[ character ] ] = true; 85 | } 86 | } 87 | 88 | for ( var i = 0, l = keys.length; i < l; i++ ) { 89 | if ( possible[ keys[i] ] ) { 90 | return origHandler.apply( this, arguments ); 91 | } 92 | } 93 | }; 94 | } 95 | 96 | jQuery.each([ "keydown", "keyup", "keypress" ], function() { 97 | jQuery.event.special[ this ] = { add: keyHandler }; 98 | }); 99 | 100 | })( jQuery ); --------------------------------------------------------------------------------