diff --git a/.gitignore b/.gitignore
index 1c0d103f3..a68fe8236 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,10 +12,10 @@ stats.json
img/
images/
private/
-docs/
vendor/
coverage/
node_modules/
bower_components/
grapesjs-*.tgz
_index.html
+docs/.vuepress/dist
diff --git a/dist/css/grapes.min.css b/dist/css/grapes.min.css
index 4f8b24c2f..4337251f7 100644
--- a/dist/css/grapes.min.css
+++ b/dist/css/grapes.min.css
@@ -1,4 +1,4 @@
.sp-container{position:absolute;top:0;left:0;display:inline-block;*display:inline;*zoom:1;z-index:9999994;overflow:hidden}.sp-container.sp-flat{position:relative}.sp-container,.sp-container *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.sp-top{position:relative;width:100%;display:inline-block}.sp-top-inner{position:absolute;top:0;left:0;bottom:0;right:0}.sp-color{position:absolute;top:0;left:0;bottom:0;right:20%}.sp-hue{position:absolute;top:0;right:0;bottom:0;left:84%;height:100%}.sp-clear-enabled .sp-hue{top:33px;height:77.5%}.sp-fill{padding-top:80%}.sp-sat,.sp-val{position:absolute;top:0;left:0;right:0;bottom:0}.sp-alpha-enabled .sp-top{margin-bottom:18px}.sp-alpha-enabled .sp-alpha{display:block}.sp-alpha-handle{position:absolute;top:-4px;bottom:-4px;width:6px;left:50%;cursor:pointer;border:1px solid black;background:white;opacity:.8}.sp-alpha{display:none;position:absolute;bottom:-14px;right:0;left:0;height:8px}.sp-alpha-inner{border:solid 1px #333}.sp-clear{display:none}.sp-clear.sp-clear-display{background-position:center}.sp-clear-enabled .sp-clear{display:block;position:absolute;top:0px;right:0;bottom:0;left:84%;height:28px}.sp-container,.sp-replacer,.sp-preview,.sp-dragger,.sp-slider,.sp-alpha,.sp-clear,.sp-alpha-handle,.sp-container.sp-dragging .sp-input,.sp-container button{-webkit-user-select:none;-moz-user-select:-moz-none;-o-user-select:none;user-select:none}.sp-container.sp-input-disabled .sp-input-container{display:none}.sp-container.sp-buttons-disabled .sp-button-container{display:none}.sp-container.sp-palette-buttons-disabled .sp-palette-button-container{display:none}.sp-palette-only .sp-picker-container{display:none}.sp-palette-disabled .sp-palette-container{display:none}.sp-initial-disabled .sp-initial{display:none}.sp-sat{background-image:-webkit-gradient(linear, 0 0, 100% 0, from(#fff), to(rgba(204,154,129,0)));background-image:-webkit-linear-gradient(left, #fff, rgba(204,154,129,0));background-image:-moz-linear-gradient(left, #fff, rgba(204,154,129,0));background-image:-o-linear-gradient(left, #fff, rgba(204,154,129,0));background-image:-ms-linear-gradient(left, #fff, rgba(204,154,129,0));background-image:linear-gradient(to right, #fff, rgba(204,154,129,0));-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)";filter:progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81')}.sp-val{background-image:-webkit-gradient(linear, 0 100%, 0 0, from(#000), to(rgba(204,154,129,0)));background-image:-webkit-linear-gradient(bottom, #000, rgba(204,154,129,0));background-image:-moz-linear-gradient(bottom, #000, rgba(204,154,129,0));background-image:-o-linear-gradient(bottom, #000, rgba(204,154,129,0));background-image:-ms-linear-gradient(bottom, #000, rgba(204,154,129,0));background-image:linear-gradient(to top, #000, rgba(204,154,129,0));-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000')}.sp-hue{background:-moz-linear-gradient(top, red 0%, #ff0 17%, lime 33%, cyan 50%, blue 67%, #f0f 83%, red 100%);background:-ms-linear-gradient(top, red 0%, #ff0 17%, lime 33%, cyan 50%, blue 67%, #f0f 83%, red 100%);background:-o-linear-gradient(top, red 0%, #ff0 17%, lime 33%, cyan 50%, blue 67%, #f0f 83%, red 100%);background:-webkit-gradient(linear, left top, left bottom, from(red), color-stop(0.17, #ff0), color-stop(0.33, lime), color-stop(0.5, cyan), color-stop(0.67, blue), color-stop(0.83, #f0f), to(red));background:-webkit-linear-gradient(top, red 0%, #ff0 17%, lime 33%, cyan 50%, blue 67%, #f0f 83%, red 100%);background:linear-gradient(to bottom, red 0%, #ff0 17%, lime 33%, cyan 50%, blue 67%, #f0f 83%, red 100%)}.sp-1{height:17%;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00')}.sp-2{height:16%;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00')}.sp-3{height:17%;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff')}.sp-4{height:17%;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff')}.sp-5{height:16%;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff')}.sp-6{height:17%;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000')}.sp-hidden{display:none !important}.sp-cf:before,.sp-cf:after{content:"";display:table}.sp-cf:after{clear:both}.sp-cf{*zoom:1}@media (max-device-width: 480px){.sp-color{right:40%}.sp-hue{left:63%}.sp-fill{padding-top:60%}}.sp-dragger{border-radius:5px;height:5px;width:5px;border:1px solid #fff;background:#000;cursor:pointer;position:absolute;top:0;left:0}.sp-slider{position:absolute;top:0;cursor:pointer;height:3px;left:-1px;right:-1px;border:1px solid #000;background:white;opacity:.8}.sp-container{border-radius:0;background-color:#ECECEC;border:solid 1px #f0c49B;padding:0}.sp-container,.sp-container button,.sp-container input,.sp-color,.sp-hue,.sp-clear{font:normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}.sp-top{margin-bottom:3px}.sp-color,.sp-hue,.sp-clear{border:solid 1px #666}.sp-input-container{float:right;width:100px;margin-bottom:4px}.sp-initial-disabled .sp-input-container{width:100%}.sp-input{font-size:12px !important;border:1px inset;padding:4px 5px;margin:0;width:100%;background:transparent;border-radius:3px;color:#222}.sp-input:focus{border:1px solid orange}.sp-input.sp-validation-error{border:1px solid red;background:#fdd}.sp-picker-container,.sp-palette-container{float:left;position:relative;padding:10px;padding-bottom:300px;margin-bottom:-290px}.sp-picker-container{width:172px;border-left:solid 1px #fff}.sp-palette-container{border-right:solid 1px #ccc}.sp-palette-only .sp-palette-container{border:0}.sp-palette .sp-thumb-el{display:block;position:relative;float:left;width:24px;height:15px;margin:3px;cursor:pointer;border:solid 2px transparent}.sp-palette .sp-thumb-el:hover,.sp-palette .sp-thumb-el.sp-thumb-active{border-color:orange}.sp-thumb-el{position:relative}.sp-initial{float:left;border:solid 1px #333}.sp-initial span{width:30px;height:25px;border:none;display:block;float:left;margin:0}.sp-initial .sp-clear-display{background-position:center}.sp-palette-button-container,.sp-button-container{float:right}.sp-replacer{margin:0;overflow:hidden;cursor:pointer;padding:4px;display:inline-block;*zoom:1;*display:inline;border:solid 1px #91765d;background:#eee;color:#333;vertical-align:middle}.sp-replacer:hover,.sp-replacer.sp-active{border-color:#F0C49B;color:#111}.sp-replacer.sp-disabled{cursor:default;border-color:silver;color:silver}.sp-dd{padding:2px 0;height:16px;line-height:16px;float:left;font-size:10px}.sp-preview{position:relative;width:25px;height:20px;border:solid 1px #222;margin-right:5px;float:left;z-index:0}.sp-palette{*width:220px;max-width:220px}.sp-palette .sp-thumb-el{width:16px;height:16px;margin:2px 1px;border:solid 1px #d0d0d0}.sp-container{padding-bottom:0}.sp-container button{background-color:#eeeeee;background-image:-webkit-linear-gradient(top, #eee, #ccc);background-image:-moz-linear-gradient(top, #eee, #ccc);background-image:-ms-linear-gradient(top, #eee, #ccc);background-image:-o-linear-gradient(top, #eee, #ccc);background-image:linear-gradient(to bottom, #eee, #ccc);border:1px solid #ccc;border-bottom:1px solid #bbb;border-radius:3px;color:#333;font-size:14px;line-height:1;padding:5px 4px;text-align:center;text-shadow:0 1px 0 #eee;vertical-align:middle}.sp-container button:hover{background-color:#dddddd;background-image:-webkit-linear-gradient(top, #ddd, #bbb);background-image:-moz-linear-gradient(top, #ddd, #bbb);background-image:-ms-linear-gradient(top, #ddd, #bbb);background-image:-o-linear-gradient(top, #ddd, #bbb);background-image:linear-gradient(to bottom, #ddd, #bbb);border:1px solid #bbb;border-bottom:1px solid #999;cursor:pointer;text-shadow:0 1px 0 #ddd}.sp-container button:active{border:1px solid #aaa;border-bottom:1px solid #888;-webkit-box-shadow:inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;-moz-box-shadow:inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;-ms-box-shadow:inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;-o-box-shadow:inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;box-shadow:inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee}.sp-cancel{font-size:11px;color:#d93f3f !important;margin:0;padding:2px;margin-right:5px;vertical-align:middle;text-decoration:none}.sp-cancel:hover{color:#d93f3f !important;text-decoration:underline}.sp-palette span:hover,.sp-palette span.sp-thumb-active{border-color:#000}.sp-preview,.sp-alpha,.sp-thumb-el{position:relative;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.sp-preview-inner,.sp-alpha-inner,.sp-thumb-inner{display:block;position:absolute;top:0;left:0;bottom:0;right:0}.sp-palette .sp-thumb-inner{background-position:50% 50%;background-repeat:no-repeat}.sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=)}.sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=)}.sp-clear-display{background-repeat:no-repeat;background-position:center;background-image:url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==)}/*!
* Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
- */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.7.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.7.0") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.7.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.7.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before{content:""}.fa-check-circle:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.CodeMirror{font-family:monospace;height:300px;color:black;direction:ltr}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:white}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:black}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid black;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0 !important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor-mark{background-color:rgba(20,255,20,0.5);-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{0%{}50%{background-color:transparent}100%{}}@-webkit-keyframes blink{0%{}50%{background-color:transparent}100%{}}@keyframes blink{0%{}50%{background-color:transparent}100%{}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:-20px;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:blue}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:bold}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3,.cm-s-default .cm-type{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:#f00}.cm-invalidchar{color:#f00}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,0.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:white}.CodeMirror-scroll{overflow:scroll !important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:none;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none !important;border:none !important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:0.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:none}.CodeMirror-scroll,.CodeMirror-sizer,.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,0.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:none}.cm-s-hopscotch.CodeMirror{background:#322931;color:#d5d3d5}.cm-s-hopscotch div.CodeMirror-selected{background:#433b42 !important}.cm-s-hopscotch .CodeMirror-gutters{background:#322931;border-right:0px}.cm-s-hopscotch .CodeMirror-linenumber{color:#797379}.cm-s-hopscotch .CodeMirror-cursor{border-left:1px solid #989498 !important}.cm-s-hopscotch span.cm-comment{color:#b33508}.cm-s-hopscotch span.cm-atom{color:#c85e7c}.cm-s-hopscotch span.cm-number{color:#c85e7c}.cm-s-hopscotch span.cm-property,.cm-s-hopscotch span.cm-attribute{color:#8fc13e}.cm-s-hopscotch span.cm-keyword{color:#dd464c}.cm-s-hopscotch span.cm-string{color:#fdcc59}.cm-s-hopscotch span.cm-variable{color:#8fc13e}.cm-s-hopscotch span.cm-variable-2{color:#1290bf}.cm-s-hopscotch span.cm-def{color:#fd8b19}.cm-s-hopscotch span.cm-error{background:#dd464c;color:#989498}.cm-s-hopscotch span.cm-bracket{color:#d5d3d5}.cm-s-hopscotch span.cm-tag{color:#dd464c}.cm-s-hopscotch span.cm-link{color:#c85e7c}.cm-s-hopscotch .CodeMirror-matchingbracket{text-decoration:underline;color:white !important}.cm-s-hopscotch .CodeMirror-activeline-background{background:#302020}@font-face{font-family:'font3336';src:url("../fonts/main-fonts.eot?v=20");src:url("../fonts/main-fonts.woff?v=20") format("woff"),url("../fonts/main-fonts.ttf?v=20") format("truetype"),url("../fonts/main-fonts.svg?v=20") format("svg"),url("../fonts/main-fonts.eot?v=20") format("embedded-opentype");font-weight:normal;font-style:normal}.gjs-one-bg{background-color:#444}.gjs-one-color{color:#444}.gjs-one-color-h:hover{color:#444}.gjs-two-bg{background-color:#ddd}.gjs-two-color{color:#ddd}.gjs-two-color-h:hover{color:#ddd}.gjs-three-bg{background-color:#804f7b}.gjs-three-color{color:#804f7b}.gjs-three-color-h:hover{color:#804f7b}.gjs-four-bg{background-color:#d278c9}.gjs-four-color{color:#d278c9}.gjs-four-color-h:hover{color:#d278c9}.gjs-bg-main,.gjs-off-prv,.gjs-sm-sector .gjs-sm-colorp-c,.gjs-clm-tags .gjs-sm-colorp-c{background-color:#444}.gjs-color-main,.gjs-off-prv,.gjs-sm-sector .gjs-sm-stack #gjs-sm-add,.gjs-clm-tags .gjs-sm-stack #gjs-sm-add{color:#ddd;fill:#ddd}.gjs-color-active{color:#f8f8f8;fill:#f8f8f8}.gjs-color-warn{color:#ffca6f;fill:#ffca6f}.gjs-color-hl{color:#71b7f1;fill:#71b7f1}.gjs-fonts::before{display:block;font:normal normal normal 14px font3336;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:5em}.gjs-f-b1::before{content:'Q'}.gjs-f-b2::before{content:'W'}.gjs-f-b3::before{content:'E'}.gjs-f-b37::before{content:'R'}.gjs-f-hero::before{content:'T'}.gjs-f-h1p::before{content:'y'}.gjs-f-3ba::before{content:'u'}.gjs-f-image::before{content:'I'}.gjs-f-text::before{content:'o'}.gjs-f-quo::before{content:'p'}.gjs-f-button::before{content:'B'}.gjs-f-divider::before{content:'D'}.gjs-invis-invis,.gjs-clm-tags #gjs-clm-new,.gjs-no-app{background-color:transparent;border:none;color:inherit}.gjs-no-app{height:10px}.gjs-test::btn{color:'#fff'}.opac50,.gjs-clm-tags #gjs-clm-close{opacity:.5;filter:alpha(opacity=50)}.gjs-checker-bg,.gjs-field-colorp-c,.checker-bg,.gjs-sm-sector .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==")}.gjs-no-user-select,.gjs-grabbing,.gjs-grabbing *,.gjs-layer-name,.gjs-rte-toolbar{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.gjs-no-pointer-events,.gjs-resizer-c,.gjs-margin-v-el,.gjs-padding-v-el,.gjs-fixedmargin-v-el,.gjs-fixedpadding-v-el{pointer-events:none}.gjs-bdrag{pointer-events:none !important;position:absolute !important;z-index:10 !important;width:auto}.gjs-drag-helper{background-color:#3b97e3 !important;pointer-events:none !important;position:absolute !important;z-index:10 !important;transform:scale(0.3) !important;transform-origin:top left !important;-webkit-transform-origin:top left !important;margin:15px !important;transition:none !important;outline:none !important}.gjs-grabbing,.gjs-grabbing *{cursor:grabbing !important;cursor:-webkit-grabbing !important}.gjs-grabbing{overflow:hidden}.gjs-off-prv{position:relative;z-index:10;padding:5px;cursor:pointer}.gjs-editor-cont ::-webkit-scrollbar-track{background:rgba(0,0,0,0.1)}.gjs-editor-cont ::-webkit-scrollbar-thumb{background-color:rgba(255,255,255,0.2)}.gjs-editor-cont ::-webkit-scrollbar{width:8px}.clear{clear:both}.no-select,.gjs-com-no-select,.gjs-com-no-select img,.gjs-category-title,.gjs-sm-sector .gjs-sm-title,.gjs-clm-tags .gjs-sm-title,.gjs-block-category .gjs-title,.gjs-layer-title,.gjs-clm-tags #gjs-clm-close{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.gjs-editor{font-family:Helvetica,sans-serif;font-size:.75rem;position:relative;box-sizing:border-box;height:100%}.gjs-freezed,.gjs-freezed{opacity:.5;filter:alpha(opacity=50);pointer-events:none}.gjs-traits-label{border-bottom:1px solid rgba(0,0,0,0.2);font-weight:lighter;margin-bottom:5px;padding:10px;text-align:left}.gjs-trt-header{font-weight:lighter;padding:10px}.gjs-trt-trait{display:flex;justify-content:start;padding:5px 10px;font-weight:lighter}.gjs-trt-traits{font-size:.75rem}.gjs-trt-trait .gjs-label{width:30%;text-align:left;text-overflow:ellipsis;overflow:hidden}.gjs-trt-trait .gjs-field{width:70%}.gjs-cv-canvas{background-color:rgba(0,0,0,0.15);box-sizing:border-box;width:85%;height:calc(100% - 40px);bottom:0;overflow:hidden;z-index:1;position:absolute;left:0;top:40px}.gjs-cv-canvas .gjs-ghost{display:none;pointer-events:none;background-color:#5b5b5b;border:2px dashed #ccc;position:absolute;z-index:10;opacity:.55;filter:alpha(opacity=55)}.gjs-cv-canvas .gjs-highlighter,.gjs-cv-canvas .gjs-highlighter-sel{position:absolute;outline:1px solid #3b97e3;outline-offset:-1px;pointer-events:none}.gjs-cv-canvas .gjs-highlighter-warning{outline:3px solid #ffca6f}.gjs-cv-canvas .gjs-highlighter-sel{outline:3px solid #3b97e3}.gjs-cv-canvas #gjs-tools{width:100%;position:absolute;top:0;left:0;outline:none}.gjs-cv-canvas>div:first-child{background-color:#fff;position:relative;height:100%;overflow:auto;width:100%}.gjs-cv-canvas *{box-sizing:border-box}.gjs-frame{outline:medium none;height:100%;width:100%;border:none;margin:auto;display:block;transition:width 0.35s ease,height 0.35s ease;position:absolute;top:0;bottom:0;left:0;right:0}.gjs-toolbar{position:absolute;background-color:#3b97e3;color:white;z-index:10}.gjs-toolbar-item{padding:5px 7px;font-size:0.8rem;cursor:pointer}.gjs-resizer-c{position:absolute;left:0;top:0;width:100%;height:100%;z-index:9}.gjs-margin-v-el,.gjs-padding-v-el,.gjs-fixedmargin-v-el,.gjs-fixedpadding-v-el{opacity:.1;filter:alpha(opacity=10);position:absolute;background-color:yellow}.gjs-fixedmargin-v-el,.gjs-fixedpadding-v-el{opacity:.2;filter:alpha(opacity=20)}.gjs-padding-v-el,.gjs-fixedpadding-v-el{background-color:navy}.gjs-resizer-h{pointer-events:all;position:absolute;border:3px solid #3b97e3;width:10px;height:10px;background-color:#fff;margin:-5px}.gjs-resizer-h-tl{top:0;left:0;cursor:nwse-resize}.gjs-resizer-h-tr{top:0;right:0;cursor:nesw-resize}.gjs-resizer-h-tc{top:0;margin:-5px auto;left:0;right:0;cursor:ns-resize}.gjs-resizer-h-cl{left:0;margin:auto -5px;top:0;bottom:0;cursor:ew-resize}.gjs-resizer-h-cr{margin:auto -5px;top:0;bottom:0;right:0;cursor:ew-resize}.gjs-resizer-h-bl{bottom:0;left:0;cursor:nesw-resize}.gjs-resizer-h-bc{bottom:0;margin:-5px auto;left:0;right:0;cursor:ns-resize}.gjs-resizer-h-br{bottom:0;right:0;cursor:nwse-resize}.gjs-pn-panel .gjs-resizer-h{background-color:rgba(0,0,0,0.2);border:none;opacity:0;transition:opacity 0.25s}.gjs-pn-panel .gjs-resizer-h:hover{opacity:1}.gjs-pn-panel .gjs-resizer-h-tc,.gjs-pn-panel .gjs-resizer-h-bc{margin:0 auto;width:100%}.gjs-pn-panel .gjs-resizer-h-cr,.gjs-pn-panel .gjs-resizer-h-cl{margin:auto 0;height:100%}.gjs-resizing .gjs-highlighter,.gjs-resizing .gjs-badge{display:none !important}.gjs-resizing-tl *{cursor:nwse-resize !important}.gjs-resizing-tr *{cursor:nesw-resize !important}.gjs-resizing-tc *{cursor:ns-resize !important}.gjs-resizing-cl *{cursor:ew-resize !important}.gjs-resizing-cr *{cursor:ew-resize !important}.gjs-resizing-bl *{cursor:nesw-resize !important}.gjs-resizing-bc *{cursor:ns-resize !important}.gjs-resizing-br *{cursor:nwse-resize !important}.btn-cl,.gjs-mdl-btn-close,.gjs-am-close{opacity:.3;filter:alpha(opacity=30);font-size:25px;cursor:pointer}.btn-cl:hover,.gjs-mdl-btn-close:hover,.gjs-am-close:hover{opacity:.7;filter:alpha(opacity=70)}.no-dots,.ui-resizable-handle{border:none !important;margin:0 !important;outline:none !important}.gjs-com-dashed *{outline:1px dashed #888;outline-offset:-2px;box-sizing:border-box}.gjs-cv-canvas .gjs-comp-selected{outline:3px solid #3b97e3 !important}*.gjs-com-hover,div.gjs-com-hover{outline:1px solid #3b97e3}*.gjs-com-hover-delete,div.gjs-com-hover-delete{outline:2px solid #dd3636;opacity:.5;filter:alpha(opacity=50)}*.gjs-com-hover-move,div.gjs-com-hover-move{outline:3px solid #ffca6f}.gjs-com-badge,.gjs-com-badge-red,.gjs-badge{pointer-events:none;background-color:#3b97e3;color:#fff;padding:2px 5px;position:absolute;z-index:1;font-size:12px;outline:none;display:none}.gjs-com-badge-red{background-color:#dd3636}.gjs-badge-warning{background-color:#ffca6f}.gjs-placeholder,.gjs-com-placeholder,.gjs-placeholder{position:absolute;z-index:10;pointer-events:none;display:none}.gjs-placeholder,.gjs-placeholder{border-style:solid !important;border-color:#62c462;outline:none;box-sizing:border-box;transition:top .2s,left .2s,width .2s,height .2s}.gjs-placeholder-int,.gjs-com-placeholder-int,.gjs-placeholder-int{background-color:#62c462;box-shadow:0 0 3px rgba(0,0,0,0.2);height:100%;width:100%;pointer-events:none;padding:1.5px;outline:none}.gjs-pn-panel{display:inline-block;position:absolute;box-sizing:border-box;text-align:center;padding:5px;z-index:3}.gjs-pn-panel .icon-undo,.gjs-pn-panel .icon-redo{font-size:20px;height:30px;width:25px}.gjs-pn-commands{width:85%;left:0;top:0;box-shadow:0 0 5px rgba(0,0,0,0.2)}.gjs-pn-options{right:15%;top:0}.gjs-pn-views{border-bottom:2px solid rgba(0,0,0,0.2);right:0;width:15%;z-index:4}.gjs-pn-views-container{height:100%;padding:42px 0 0;right:0;width:15%;overflow:auto;box-shadow:0 0 5px rgba(0,0,0,0.2)}.gjs-pn-buttons{align-items:center;display:flex;justify-content:space-between}.gjs-pn-btn{box-sizing:border-box;height:30px;width:30px;line-height:21px;background-color:transparent;border:none;font-size:18px;margin-right:5px;border-radius:2px;padding:5px;position:relative;cursor:pointer}.gjs-pn-btn.gjs-pn-active{background-color:rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.25) inset}.gjs-comp-image-placeholder{display:block;background-color:#f5f5f5;color:#777;height:50px;width:50px;line-height:50px;outline:3px solid #ffca6f;outline-offset:-3px;text-align:center;font-size:16.66667px;cursor:pointer}.gjs-comp-image-placeholder.fa-picture-o::after{content:"\f03e"}.gjs-label{line-height:18px}.gjs-fields{display:flex}.gjs-select{padding:0;width:100%}.gjs-select select{padding-right:10px}.gjs-select:-moz-focusring,.gjs-select select:-moz-focusring{color:transparent;text-shadow:0 0 0 rgba(255,255,255,0.7)}.gjs-input:focus,.gjs-button:focus,.gjs-btn-prim:focus,.gjs-select:focus,.gjs-select select:focus{outline:none}.gjs-field input,.gjs-field select,.gjs-field textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;color:inherit;border:none;background-color:transparent;box-sizing:border-box;width:100%;position:relative;padding:5px;z-index:1}.gjs-field input:focus,.gjs-field select:focus,.gjs-field textarea:focus{outline:none}.gjs-field-range{flex:9 1 auto}.gjs-field-integer input{padding-right:30px}.gjs-select option,.gjs-clm-select option,.gjs-sm-select option,.gjs-fields option,.gjs-sm-unit option{background-color:#444;color:#ddd}.gjs-field{background-color:rgba(0,0,0,0.2);border:none;box-shadow:none;border-radius:2px;box-sizing:border-box;padding:0;position:relative}.gjs-field textarea{resize:vertical}.gjs-field .gjs-sel-arrow{height:100%;width:9px;position:absolute;right:0;top:0;z-index:0}.gjs-field .gjs-d-s-arrow{bottom:0;top:0;margin:auto;right:5px;border-top:4px solid rgba(255,255,255,0.7);position:absolute;height:0;width:0;border-left:3px solid transparent;border-right:4px solid transparent;cursor:pointer}.gjs-field-arrows{position:absolute;cursor:ns-resize;margin:auto;height:20px;width:9px;z-index:10;bottom:0;right:3px;top:0}.gjs-field-color,.gjs-field-radio{width:100%}.gjs-field-color input{padding-right:22px;box-sizing:border-box}.gjs-field-colorp{border-left:1px solid rgba(0,0,0,0.2);box-sizing:border-box;height:100%;padding:2px;position:absolute;right:0;top:0;width:22px;z-index:10}.gjs-field-colorp .gjs-checker-bg,.gjs-field-colorp .gjs-field-colorp-c{height:100%;width:100%;border-radius:1px}.gjs-field-colorp-c{height:100%;position:relative;width:100%}.gjs-field-color-picker{background-color:#ddd;cursor:pointer;height:100%;width:100%;box-shadow:0 0 1px rgba(0,0,0,0.2);border-radius:1px;position:absolute;top:0}.gjs-field-checkbox{padding:0;width:auto !important}.gjs-field-checkbox input{display:none}.gjs-field-checkbox input:checked+.gjs-chk-icon{border-color:rgba(255,255,255,0.5);border-width:0 2px 2px 0;border-style:solid}.gjs-radio-item{flex:1 1 auto;text-align:center;border-left:1px solid rgba(0,0,0,0.2)}.gjs-radio-item:first-child{border:none}.gjs-radio-item:hover{background:rgba(0,0,0,0.2)}.gjs-radio-item input{display:none}.gjs-radio-item input:checked+.gjs-radio-item-label{background-color:rgba(255,255,255,0.2)}.gjs-radio-items{display:flex}.gjs-radio-item-label{cursor:pointer;display:block;padding:5px}.gjs-field-units{position:absolute;margin:auto;right:10px;bottom:0;top:0}.gjs-field-unit{position:absolute;right:10px;top:3px;font-size:10px;color:rgba(255,255,255,0.7);cursor:pointer}.gjs-field-arrow-u,.gjs-field-arrow-d{position:absolute;height:0;width:0;border-left:3px solid transparent;border-right:4px solid transparent;border-top:4px solid rgba(255,255,255,0.7);bottom:4px;cursor:pointer}.gjs-field-arrow-u{border-bottom:4px solid rgba(255,255,255,0.7);border-top:none;top:4px}.gjs-field-select{padding:0}.gjs-field-range{background-color:transparent;border:none;box-shadow:none;padding:0}.gjs-field-range input{margin:0;height:100%}.gjs-field-range input:focus{outline:none}.gjs-field-range input::-webkit-slider-thumb{-webkit-appearance:none;margin-top:-4px;height:10px;width:10px;border:1px solid rgba(0,0,0,0.2);border-radius:100%;background-color:#ddd;cursor:pointer}.gjs-field-range input::-moz-range-thumb{height:10px;width:10px;border:1px solid rgba(0,0,0,0.2);border-radius:100%;background-color:#ddd;cursor:pointer}.gjs-field-range input::-ms-thumb{height:10px;width:10px;border:1px solid rgba(0,0,0,0.2);border-radius:100%;background-color:#ddd;cursor:pointer}.gjs-field-range input::-moz-range-track{background-color:rgba(0,0,0,0.2);border-radius:1px;margin-top:3px;height:3px}.gjs-field-range input::-webkit-slider-runnable-track{background-color:rgba(0,0,0,0.2);border-radius:1px;margin-top:3px;height:3px}.gjs-field-range input::-ms-track{background-color:rgba(0,0,0,0.2);border-radius:1px;margin-top:3px;height:3px}.gjs-btn-prim{color:inherit;background-color:rgba(255,255,255,0.1);border-radius:2px;padding:3px 6px;padding:5px;cursor:pointer;border:none}.gjs-btn-prim:active{background-color:rgba(255,255,255,0.1)}.gjs-chk-icon{-ms-transform:rotate(45deg);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);transform:rotate(45deg);box-sizing:border-box;display:block;height:14px;margin:0 5px;width:6px;cursor:pointer}.gjs-add-trasp{background:none;border:none;color:#ddd;cursor:pointer;font-size:1em;border-radius:2px;opacity:.75;filter:alpha(opacity=75)}.gjs-add-trasp:hover{opacity:1;filter:alpha(opacity=100)}.gjs-add-trasp:active{background-color:rgba(0,0,0,0.2)}.gjs-devices-c{display:flex;align-items:center;padding:3px}.gjs-devices-c .gjs-device-label{flex-grow:2;text-align:left;margin-right:10px}.gjs-devices-c .gjs-select{flex-grow:20}.gjs-devices-c .gjs-add-trasp{flex-grow:1;margin-left:5px}.gjs-category-open,.gjs-sm-sector.gjs-sm-open,.gjs-sm-open.gjs-clm-tags,.gjs-block-category.gjs-open{border-bottom:1px solid rgba(0,0,0,0.25)}.gjs-category-title,.gjs-sm-sector .gjs-sm-title,.gjs-clm-tags .gjs-sm-title,.gjs-block-category .gjs-title,.gjs-layer-title{font-weight:lighter;background-color:rgba(0,0,0,0.1);letter-spacing:1px;padding:9px 10px 9px 20px;border-bottom:1px solid rgba(0,0,0,0.25);text-align:left;position:relative;cursor:pointer}.gjs-sm-clear{line-height:0;cursor:pointer}.gjs-sm-header{font-weight:lighter;padding:10px}.gjs-sm-properties{font-size:.75rem;padding:10px 5px;display:flex;flex-wrap:wrap;align-items:flex-end;box-sizing:border-box;width:100%}.gjs-sm-label{margin:5px 5px 3px 0}.gjs-sm-close-btn,.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close{display:block;font-size:23px;position:absolute;cursor:pointer;right:5px;top:0;opacity:.2;filter:alpha(opacity=20)}.gjs-sm-close-btn:hover,.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close:hover,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close:hover{opacity:.7;filter:alpha(opacity=70)}.gjs-sm-sector,.gjs-clm-tags{clear:both;font-weight:lighter;text-align:left}.gjs-sm-sector #gjs-sm-caret,.gjs-clm-tags #gjs-sm-caret{padding-right:5px;font-size:11px}.gjs-sm-sector .gjs-sm-field,.gjs-clm-tags .gjs-sm-field,.gjs-sm-sector .gjs-clm-field,.gjs-clm-tags .gjs-clm-field{width:100%;position:relative}.gjs-sm-sector .gjs-sm-field input,.gjs-clm-tags .gjs-sm-field input,.gjs-sm-sector .gjs-clm-field input,.gjs-clm-tags .gjs-clm-field input,.gjs-sm-sector .gjs-sm-field select,.gjs-clm-tags .gjs-sm-field select,.gjs-sm-sector .gjs-clm-field select,.gjs-clm-tags .gjs-clm-field select{background-color:transparent;color:rgba(255,255,255,0.7);border:none;width:100%}.gjs-sm-sector .gjs-sm-field input,.gjs-clm-tags .gjs-sm-field input,.gjs-sm-sector .gjs-clm-field input,.gjs-clm-tags .gjs-clm-field input{box-sizing:border-box}.gjs-sm-sector .gjs-sm-field select,.gjs-clm-tags .gjs-sm-field select,.gjs-sm-sector .gjs-clm-field select,.gjs-clm-tags .gjs-clm-field select{position:relative;z-index:1;-webkit-appearance:none;-moz-appearance:none;appearance:none}.gjs-sm-sector .gjs-sm-field select::-ms-expand,.gjs-clm-tags .gjs-sm-field select::-ms-expand,.gjs-sm-sector .gjs-clm-field select::-ms-expand,.gjs-clm-tags .gjs-clm-field select::-ms-expand{display:none}.gjs-sm-sector .gjs-sm-field select:-moz-focusring,.gjs-clm-tags .gjs-sm-field select:-moz-focusring,.gjs-sm-sector .gjs-clm-field select:-moz-focusring,.gjs-clm-tags .gjs-clm-field select:-moz-focusring{color:transparent;text-shadow:0 0 0 rgba(255,255,255,0.7)}.gjs-sm-sector .gjs-sm-field input:focus,.gjs-clm-tags .gjs-sm-field input:focus,.gjs-sm-sector .gjs-clm-field input:focus,.gjs-clm-tags .gjs-clm-field input:focus,.gjs-sm-sector .gjs-sm-field select:focus,.gjs-clm-tags .gjs-sm-field select:focus,.gjs-sm-sector .gjs-clm-field select:focus,.gjs-clm-tags .gjs-clm-field select:focus{outline:none}.gjs-sm-sector .gjs-sm-field .gjs-sm-unit,.gjs-clm-tags .gjs-sm-field .gjs-sm-unit,.gjs-sm-sector .gjs-clm-field .gjs-sm-unit,.gjs-clm-tags .gjs-clm-field .gjs-sm-unit{position:absolute;right:10px;top:3px;font-size:10px;color:rgba(255,255,255,0.7);cursor:pointer}.gjs-sm-sector .gjs-sm-field .gjs-clm-sel-arrow,.gjs-clm-tags .gjs-sm-field .gjs-clm-sel-arrow,.gjs-sm-sector .gjs-clm-field .gjs-clm-sel-arrow,.gjs-clm-tags .gjs-clm-field .gjs-clm-sel-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-int-arrows,.gjs-clm-tags .gjs-sm-field .gjs-sm-int-arrows,.gjs-sm-sector .gjs-clm-field .gjs-sm-int-arrows,.gjs-clm-tags .gjs-clm-field .gjs-sm-int-arrows,.gjs-sm-sector .gjs-sm-field .gjs-sm-sel-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-sel-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-sel-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-sel-arrow{height:100%;width:9px;position:absolute;right:0;top:0;cursor:ns-resize}.gjs-sm-sector .gjs-sm-field .gjs-sm-sel-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-sel-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-sel-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-sel-arrow{cursor:pointer}.gjs-sm-sector .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-d-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-d-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-d-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-d-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-d-s-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-u-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-u-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-u-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-u-arrow{position:absolute;height:0;width:0;border-left:3px solid transparent;border-right:4px solid transparent;cursor:pointer}.gjs-sm-sector .gjs-sm-field .gjs-sm-u-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-u-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-u-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-u-arrow{border-bottom:4px solid rgba(255,255,255,0.7);top:4px}.gjs-sm-sector .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-d-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-d-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-d-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-d-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-d-s-arrow{border-top:4px solid rgba(255,255,255,0.7);bottom:4px}.gjs-sm-sector .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-d-s-arrow{bottom:7px}.gjs-sm-sector .gjs-sm-field.gjs-sm-color,.gjs-clm-tags .gjs-sm-field.gjs-sm-color,.gjs-sm-sector .gjs-sm-color.gjs-clm-field,.gjs-clm-tags .gjs-sm-color.gjs-clm-field,.gjs-sm-sector .gjs-sm-field.gjs-sm-input,.gjs-clm-tags .gjs-sm-field.gjs-sm-input,.gjs-sm-sector .gjs-sm-input.gjs-clm-field,.gjs-clm-tags .gjs-sm-input.gjs-clm-field,.gjs-sm-sector .gjs-sm-field.gjs-sm-integer,.gjs-clm-tags .gjs-sm-field.gjs-sm-integer,.gjs-sm-sector .gjs-sm-integer.gjs-clm-field,.gjs-clm-tags .gjs-sm-integer.gjs-clm-field,.gjs-sm-sector .gjs-sm-field.gjs-sm-list,.gjs-clm-tags .gjs-sm-field.gjs-sm-list,.gjs-sm-sector .gjs-sm-list.gjs-clm-field,.gjs-clm-tags .gjs-sm-list.gjs-clm-field,.gjs-sm-sector .gjs-sm-field.gjs-sm-select,.gjs-clm-tags .gjs-sm-field.gjs-sm-select,.gjs-sm-sector .gjs-sm-select.gjs-clm-field,.gjs-clm-tags .gjs-sm-select.gjs-clm-field,.gjs-sm-sector .gjs-clm-select,.gjs-clm-tags .gjs-clm-select{background-color:rgba(0,0,0,0.2);border:1px solid rgba(0,0,0,0.1);box-shadow:1px 1px 0 rgba(255,255,255,0.1);color:rgba(255,255,255,0.7);border-radius:2px;box-sizing:border-box;padding:0 5px}.gjs-sm-sector .gjs-sm-field.gjs-sm-composite,.gjs-clm-tags .gjs-sm-field.gjs-sm-composite,.gjs-sm-sector .gjs-sm-composite.gjs-clm-field,.gjs-clm-tags .gjs-sm-composite.gjs-clm-field{border-radius:2px}.gjs-sm-sector .gjs-sm-field.gjs-sm-select,.gjs-clm-tags .gjs-sm-field.gjs-sm-select,.gjs-sm-sector .gjs-sm-select.gjs-clm-field,.gjs-clm-tags .gjs-sm-select.gjs-clm-field,.gjs-sm-sector .gjs-clm-select,.gjs-clm-tags .gjs-clm-select{padding:0}.gjs-sm-sector .gjs-sm-field.gjs-sm-select select,.gjs-clm-tags .gjs-sm-field.gjs-sm-select select,.gjs-sm-sector .gjs-sm-select.gjs-clm-field select,.gjs-clm-tags .gjs-sm-select.gjs-clm-field select,.gjs-sm-sector .gjs-clm-select select,.gjs-clm-tags .gjs-clm-select select{height:20px}.gjs-sm-sector .gjs-sm-field.gjs-sm-select option,.gjs-clm-tags .gjs-sm-field.gjs-sm-select option,.gjs-sm-sector .gjs-sm-select.gjs-clm-field option,.gjs-clm-tags .gjs-sm-select.gjs-clm-field option,.gjs-sm-sector .gjs-clm-select option,.gjs-clm-tags .gjs-clm-select option{padding:3px 0}.gjs-sm-sector .gjs-sm-field.gjs-sm-composite,.gjs-clm-tags .gjs-sm-field.gjs-sm-composite,.gjs-sm-sector .gjs-sm-composite.gjs-clm-field,.gjs-clm-tags .gjs-sm-composite.gjs-clm-field{background-color:rgba(0,0,0,0.1);border:1px solid rgba(0,0,0,0.25)}.gjs-sm-sector .gjs-sm-field.gjs-sm-list,.gjs-clm-tags .gjs-sm-field.gjs-sm-list,.gjs-sm-sector .gjs-sm-list.gjs-clm-field,.gjs-clm-tags .gjs-sm-list.gjs-clm-field{width:auto;padding:0;overflow:hidden;float:left}.gjs-sm-sector .gjs-sm-field.gjs-sm-list input,.gjs-clm-tags .gjs-sm-field.gjs-sm-list input,.gjs-sm-sector .gjs-sm-list.gjs-clm-field input,.gjs-clm-tags .gjs-sm-list.gjs-clm-field input{display:none}.gjs-sm-sector .gjs-sm-field.gjs-sm-list label,.gjs-clm-tags .gjs-sm-field.gjs-sm-list label,.gjs-sm-sector .gjs-sm-list.gjs-clm-field label,.gjs-clm-tags .gjs-sm-list.gjs-clm-field label{cursor:pointer;padding:5px;display:block}.gjs-sm-sector .gjs-sm-field.gjs-sm-list .gjs-sm-radio:checked+label,.gjs-clm-tags .gjs-sm-field.gjs-sm-list .gjs-sm-radio:checked+label,.gjs-sm-sector .gjs-sm-list.gjs-clm-field .gjs-sm-radio:checked+label,.gjs-clm-tags .gjs-sm-list.gjs-clm-field .gjs-sm-radio:checked+label{background-color:rgba(255,255,255,0.2)}.gjs-sm-sector .gjs-sm-field.gjs-sm-list .gjs-sm-icon,.gjs-clm-tags .gjs-sm-field.gjs-sm-list .gjs-sm-icon,.gjs-sm-sector .gjs-sm-list.gjs-clm-field .gjs-sm-icon,.gjs-clm-tags .gjs-sm-list.gjs-clm-field .gjs-sm-icon{background-repeat:no-repeat;background-position:center;text-shadow:none;line-height:normal}.gjs-sm-sector .gjs-sm-field.gjs-sm-integer select,.gjs-clm-tags .gjs-sm-field.gjs-sm-integer select,.gjs-sm-sector .gjs-sm-integer.gjs-clm-field select,.gjs-clm-tags .gjs-sm-integer.gjs-clm-field select{width:auto;padding:0}.gjs-sm-sector .gjs-sm-list .gjs-sm-el,.gjs-clm-tags .gjs-sm-list .gjs-sm-el{float:left;border-left:1px solid rgba(0,0,0,0.2)}.gjs-sm-sector .gjs-sm-list .gjs-sm-el:first-child,.gjs-clm-tags .gjs-sm-list .gjs-sm-el:first-child{border:none}.gjs-sm-sector .gjs-sm-list .gjs-sm-el:hover,.gjs-clm-tags .gjs-sm-list .gjs-sm-el:hover{background:rgba(0,0,0,0.2)}.gjs-sm-sector .gjs-sm-slider .gjs-field-integer,.gjs-clm-tags .gjs-sm-slider .gjs-field-integer{flex:1 1 65px}.gjs-sm-sector .gjs-sm-property,.gjs-clm-tags .gjs-sm-property{box-sizing:border-box;float:left;width:50%;margin-bottom:5px;padding:0 5px}.gjs-sm-sector .gjs-sm-property.gjs-sm-composite,.gjs-clm-tags .gjs-sm-property.gjs-sm-composite,.gjs-sm-sector .gjs-sm-property.gjs-sm-file,.gjs-clm-tags .gjs-sm-property.gjs-sm-file,.gjs-sm-sector .gjs-sm-property.gjs-sm-list,.gjs-clm-tags .gjs-sm-property.gjs-sm-list,.gjs-sm-sector .gjs-sm-property.gjs-sm-radio,.gjs-clm-tags .gjs-sm-property.gjs-sm-radio,.gjs-sm-sector .gjs-sm-property.gjs-sm-stack,.gjs-clm-tags .gjs-sm-property.gjs-sm-stack,.gjs-sm-sector .gjs-sm-property.gjs-sm-slider,.gjs-clm-tags .gjs-sm-property.gjs-sm-slider,.gjs-sm-sector .gjs-sm-property.gjs-sm-color,.gjs-clm-tags .gjs-sm-property.gjs-sm-color{width:100%}.gjs-sm-sector .gjs-sm-property .gjs-sm-btn,.gjs-clm-tags .gjs-sm-property .gjs-sm-btn{background-color:rgba(33,33,33,0.2);border-radius:2px;box-shadow:1px 1px 0 rgba(5,5,5,0.2),1px 1px 0 rgba(43,43,43,0.2) inset;padding:5px;position:relative;text-align:center;height:auto;width:100%;cursor:pointer;color:#ddd;box-sizing:border-box;text-shadow:-1px -1px 0 rgba(0,0,0,0.2);border:none;opacity:.85;filter:alpha(opacity=85)}.gjs-sm-sector .gjs-sm-property .gjs-sm-btn-c,.gjs-clm-tags .gjs-sm-property .gjs-sm-btn-c{box-sizing:border-box;float:left;width:100%}.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box{background-color:rgba(255,255,255,0.05);border-radius:2px;margin-top:5px;position:relative;overflow:hidden}.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box.gjs-sm-show,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box.gjs-sm-show{border:1px solid rgba(252,252,252,0.05);padding:3px 20px}.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close{top:-5px;display:block}.gjs-sm-sector .gjs-sm-property.gjs-sm-file .gjs-sm-show #gjs-sm-preview-file,.gjs-clm-tags .gjs-sm-property.gjs-sm-file .gjs-sm-show #gjs-sm-preview-file{height:50px}.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-file,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-file{background-size:auto 100%;background-repeat:no-repeat;background-position:center center}.gjs-sm-sector .gjs-sm-property .gjs-sm-layers,.gjs-clm-tags .gjs-sm-property .gjs-sm-layers{margin-top:5px;padding:1px 3px;min-height:30px}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer{background-color:rgba(255,255,255,0.055);border-radius:2px;margin:2px 0;padding:7px;position:relative;cursor:pointer}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box{height:15px;position:absolute;right:27px;top:6px;width:15px}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer #gjs-sm-preview,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer #gjs-sm-preview,.gjs-sm-sector .gjs-sm-property .gjs-sm-layer #gjs-sm-preview-box,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer #gjs-sm-preview-box{border-radius:2px}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer #gjs-sm-close-layer,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer #gjs-sm-close-layer{display:block;font-size:23px;position:absolute;cursor:pointer;right:5px;top:0;opacity:.5;filter:alpha(opacity=50)}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer #gjs-sm-close-layer:hover,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer #gjs-sm-close-layer:hover{opacity:.8;filter:alpha(opacity=80)}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box #gjs-sm-preview,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box #gjs-sm-preview{background-color:white;height:100%;width:100%;background-size:cover !important}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer.gjs-sm-active,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer.gjs-sm-active{background-color:rgba(255,255,255,0.12)}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer.gjs-sm-no-preview #gjs-sm-preview-box,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer.gjs-sm-no-preview #gjs-sm-preview-box{display:none}.gjs-sm-sector #gjs-sm-text-shadow #gjs-sm-preview::after,.gjs-clm-tags #gjs-sm-text-shadow #gjs-sm-preview::after{color:#000;content:"T";font-weight:900;line-height:17px;padding:0 4px}.gjs-sm-sector .gjs-sm-stack .gjs-sm-properties,.gjs-clm-tags .gjs-sm-stack .gjs-sm-properties{padding:5px 0 0}.gjs-sm-sector .gjs-sm-stack #gjs-sm-add,.gjs-clm-tags .gjs-sm-stack #gjs-sm-add{background:none;border:none;cursor:pointer;outline:none;font-size:22px;line-height:10px;position:absolute;right:0;top:-17px;opacity:0.75}.gjs-sm-sector .gjs-sm-stack #gjs-sm-add:hover,.gjs-clm-tags .gjs-sm-stack #gjs-sm-add:hover{opacity:1;filter:alpha(opacity=100)}.gjs-sm-sector .gjs-sm-colorp-c,.gjs-clm-tags .gjs-sm-colorp-c{height:100%;width:20px;position:absolute;right:0;top:0;box-sizing:border-box;border-radius:2px;padding:2px}.gjs-sm-sector .gjs-sm-colorp-c .gjs-checker-bg,.gjs-clm-tags .gjs-sm-colorp-c .gjs-checker-bg,.gjs-sm-sector .gjs-sm-colorp-c .gjs-field-colorp-c,.gjs-clm-tags .gjs-sm-colorp-c .gjs-field-colorp-c{height:100%;width:100%;border-radius:1px}.gjs-sm-sector .gjs-sm-color-picker,.gjs-clm-tags .gjs-sm-color-picker{background-color:#ddd;cursor:pointer;height:16px;width:100%;margin-top:-16px;box-shadow:0 0 1px rgba(0,0,0,0.2);border-radius:1px}.gjs-sm-sector .gjs-sm-btn-upload #gjs-sm-upload,.gjs-clm-tags .gjs-sm-btn-upload #gjs-sm-upload{left:0;top:0;position:absolute;width:100%;opacity:0;cursor:pointer}.gjs-sm-sector .gjs-sm-btn-upload #gjs-sm-label,.gjs-clm-tags .gjs-sm-btn-upload #gjs-sm-label{padding:2px 0}.gjs-sm-layer>#gjs-sm-move{opacity:.7;filter:alpha(opacity=70);cursor:move;font-size:12px;float:left;margin:0 5px 0 0}.gjs-sm-layer>#gjs-sm-move:hover{opacity:.9;filter:alpha(opacity=90)}.gjs-blocks-c{display:flex;flex-wrap:wrap;justify-content:flex-start}.gjs-block-categories{display:flex;flex-direction:column}.gjs-block-category{width:100%}.gjs-block-category .gjs-caret-icon{margin-right:5px}.gjs-block{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;width:45%;padding:1em;box-sizing:border-box;min-height:90px;cursor:all-scroll;font-size:11px;font-weight:lighter;text-align:center;display:flex;flex-direction:column;justify-content:space-between;border:1px solid rgba(0,0,0,0.2);border-radius:3px;margin:10px 2.5% 5px;box-shadow:0 1px 0 0 rgba(0,0,0,0.15);transition:all 0.2s ease 0s;transition-property:box-shadow, color}.gjs-block:hover{box-shadow:0 3px 4px 0 rgba(0,0,0,0.15)}.gjs-block-svg{width:54px;fill:currentColor}.gjs-block-svg-path{fill:currentColor}.gjs-block.fa{font-size:2em;line-height:2em;padding:11px}.gjs-block-label{line-height:normal;font-size:0.65rem;font-weight:normal;font-family:Helvetica, sans-serif;overflow:hidden;text-overflow:ellipsis}.gjs-block.gjs-bdrag{width:auto;padding:0}.gjs-selected-parent{border:1px solid #ffca6f}.gjs-opac50{opacity:.5;filter:alpha(opacity=50)}.gjs-layer{font-weight:lighter;text-align:left;position:relative;background-color:rgba(0,0,0,0.1);font-size:.75rem}.gjs-layer-hidden{opacity:.55;filter:alpha(opacity=55)}.gjs-layer-count{position:absolute;right:27px;top:9px}.gjs-layer-vis{height:auto !important;width:auto !important;left:0;top:0;padding:7px 5px 7px 10px;position:absolute;cursor:pointer;z-index:1}.gjs-layer-caret{font-size:0.5rem;width:8px;padding:2px;cursor:pointer;position:absolute;left:-9px;top:6px;opacity:.7;filter:alpha(opacity=70)}.gjs-layer-caret:hover{opacity:1;filter:alpha(opacity=100)}.gjs-layer-title{padding:3px 10px 5px 30px;display:flex;align-items:center}.gjs-layer-title-inn{position:relative}.gjs-layer-name{padding:5px;display:inline-block;box-sizing:content-box}.gjs-layer>.gjs-layer-children{display:none}.gjs-layer.open>.gjs-layer-children{display:block}.gjs-layer-no-chld>.gjs-layer-title-inn>.gjs-layer-caret{display:none}.gjs-layer-move{padding:7px 10px 7px 5px;position:absolute;font-size:12px;cursor:move;right:0;top:0}.gjs-layer.gjs-hovered .gjs-layer-title{background-color:rgba(255,255,255,0.015)}.gjs-layer.gjs-selected .gjs-layer-title{background-color:rgba(255,255,255,0.1)}.gjs-layers{position:relative;height:100%}.gjs-layers #gjs-placeholder{width:100%;position:absolute}.gjs-layers #gjs-placeholder #gjs-plh-int{height:100%;padding:1px}.gjs-layers #gjs-placeholder #gjs-plh-int.gjs-insert{background-color:#62c462}.gjs-clm-tags{font-size:.75rem;padding:10px 5px}.gjs-clm-tags #gjs-clm-label,.gjs-clm-tags #gjs-clm-sel{padding:7px 0;float:left}.gjs-clm-tags #gjs-clm-sel{font-style:italic;margin-left:5px}.gjs-clm-tags #gjs-clm-status-c{float:right}.gjs-clm-tags #gjs-clm-tags-field{clear:both;padding:5px;margin-bottom:5px}.gjs-clm-tags #gjs-clm-tags-c{display:inline-block;vertical-align:top}.gjs-clm-tags #gjs-clm-add-tag{background-color:rgba(255,255,255,0.15);border-radius:2px;padding:5px 6px;box-shadow:1px 1px 0 rgba(255,255,255,0.2) inset;border:1px solid rgba(0,0,0,0.15);cursor:pointer}.gjs-clm-tags #gjs-clm-new{color:#ddd;padding:5px 6px;display:none}.gjs-clm-tags .gjs-clm-tag{display:inline-block;border-radius:3px;margin:0 3px 3px 0;padding:5px;cursor:default}.gjs-clm-tags #gjs-clm-close{font-size:20px;line-height:0;cursor:pointer;color:rgba(255,255,255,0.9)}.gjs-clm-tags #gjs-clm-close:hover{opacity:.7;filter:alpha(opacity=70)}.gjs-clm-tags #gjs-clm-checkbox{color:rgba(255,255,255,0.9);vertical-align:middle;cursor:pointer;font-size:9px}.gjs-clm-tags #gjs-clm-tag-label{padding:0 3px;cursor:text}.gjs-mdl-backlayer{opacity:.5;filter:alpha(opacity=50);background-color:#000;position:absolute;top:0;left:0;z-index:1;width:100%;height:100%}.gjs-mdl-container{font-family:Helvetica,sans-serif;position:absolute;top:0;left:0;z-index:10;width:100%;height:100%}.gjs-mdl-dialog{text-shadow:-1px -1px 0 rgba(0,0,0,0.05);animation:gjs-slide-down 0.215s;margin:30px auto 0;max-width:850px;width:90%;border-radius:3px;font-weight:lighter;position:relative;z-index:2}.gjs-mdl-title{font-size:1rem}.gjs-mdl-btn-close{position:absolute;right:15px;top:5px}.gjs-mdl-active .gjs-mdl-dialog{animation:gjs-mdl-slide-down 0.216s}.gjs-mdl-header,.gjs-mdl-content{padding:10px 15px;clear:both}.gjs-mdl-header{position:relative;border-bottom:1px solid rgba(0,0,0,0.2);padding:15px 15px 7px}.gjs-export-dl::after{content:"";clear:both;display:block;margin-bottom:10px}.gjs-dropzone{display:none;opacity:0;position:absolute;top:0;left:0;z-index:11;width:100%;height:100%;transition:opacity 0.25s;pointer-events:none}.gjs-dropzone-active .gjs-dropzone{display:block;opacity:1}.gjs-am-assets{height:290px;overflow:auto;clear:both;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:flex-start}.gjs-am-assets-header{padding:5px}.gjs-am-add-asset .gjs-am-add-field{width:70%;float:left}.gjs-am-add-asset button{width:25%;float:right}.gjs-am-preview-cont{position:relative;height:70px;width:30%;background-color:#444;border-radius:2px;float:left;overflow:hidden}.gjs-am-preview{position:absolute;background-position:center center;background-size:cover;background-repeat:no-repeat;height:100%;width:100%;z-index:1}.gjs-am-preview-bg{opacity:.5;filter:alpha(opacity=50);position:absolute;height:100%;width:100%;z-index:0}.gjs-am-dimensions{opacity:.5;filter:alpha(opacity=50);font-size:10px}.gjs-am-meta{width:70%;float:left;font-size:12px;padding:5px 0 0 5px;box-sizing:border-box}.gjs-am-meta>div{margin-bottom:5px}.gjs-am-close{cursor:pointer;position:absolute;right:5px;top:0;display:none}.gjs-am-asset{border-bottom:1px solid rgba(0,0,0,0.2);padding:5px;cursor:pointer;position:relative;box-sizing:border-box;width:100%}.gjs-am-asset:hover .gjs-am-close{display:block}.gjs-am-highlight{background-color:rgba(255,255,255,0.1)}.gjs-am-assets-cont{background-color:rgba(0,0,0,0.1);border-radius:3px;box-sizing:border-box;padding:10px;width:45%;float:right;height:325px;overflow:hidden}.gjs-am-file-uploader{width:55%;float:left}.gjs-am-file-uploader>form{background-color:rgba(0,0,0,0.1);border:2px dashed;border-radius:3px;position:relative;text-align:center;margin-bottom:15px}.gjs-am-file-uploader>form.gjs-am-hover{border:2px solid #62c462;color:#75cb75}.gjs-am-file-uploader>form.gjs-am-disabled{border-color:red}.gjs-am-file-uploader>form #gjs-am-uploadFile{opacity:0;filter:alpha(opacity=0);padding:150px 10px;width:100%;box-sizing:border-box}.gjs-am-file-uploader #gjs-am-title{position:absolute;padding:150px 10px;width:100%}.gjs-cm-editor-c{float:left;box-sizing:border-box;width:50%}.gjs-cm-editor-c .CodeMirror{height:450px}.gjs-cm-editor{font-size:12px}.gjs-cm-editor#gjs-cm-htmlmixed{padding-right:10px;border-right:1px solid rgba(0,0,0,0.2)}.gjs-cm-editor#gjs-cm-htmlmixed #gjs-cm-title{color:#a97d44}.gjs-cm-editor#gjs-cm-css{padding-left:10px}.gjs-cm-editor#gjs-cm-css #gjs-cm-title{color:#ddca7e}.gjs-cm-editor #gjs-cm-title{background-color:rgba(0,0,0,0.2);font-size:12px;padding:5px 10px 3px;text-align:right}.gjs-rte-toolbar{border:1px solid rgba(0,0,0,0.2);position:absolute;border-radius:3px;z-index:10}.gjs-rte-actionbar{display:flex}.gjs-rte-action{display:flex;align-items:center;justify-content:center;padding:5px;min-width:25px;border-right:1px solid rgba(0,0,0,0.2);text-align:center;cursor:pointer;outline:none}.gjs-rte-action:last-child{border-right:none}.gjs-rte-action:hover{background-color:rgba(255,255,255,0.1)}.gjs-rte-active{background-color:rgba(0,0,0,0.2)}.gjs-editor-cont .sp-hue,.gjs-editor-cont .sp-slider{cursor:row-resize}.gjs-editor-cont .sp-color,.gjs-editor-cont .sp-dragger{cursor:crosshair}.gjs-editor-cont .sp-alpha-inner,.gjs-editor-cont .sp-alpha-handle{cursor:col-resize}.gjs-editor-cont .sp-hue{left:90%}.gjs-editor-cont .sp-color{right:15%}.gjs-editor-cont .sp-container{border:1px solid rgba(0,0,0,0.2);box-shadow:0 0 7px rgba(0,0,0,0.2);border-radius:3px}.gjs-editor-cont .sp-picker-container{border:none}.gjs-editor-cont .colpick_dark .colpick_color{outline:1px solid rgba(0,0,0,0.2)}.gjs-editor-cont .sp-cancel,.gjs-editor-cont .sp-cancel:hover{bottom:-8px;color:#777 !important;font-size:25px;left:0;position:absolute;text-decoration:none}.gjs-editor-cont .sp-alpha-handle{background-color:#ccc;border:1px solid #555;width:4px}.gjs-editor-cont .sp-color,.gjs-editor-cont .sp-hue{border:1px solid #333333}.gjs-editor-cont .sp-slider{background-color:#ccc;border:1px solid #555;height:3px;left:-4px;width:22px}.gjs-editor-cont .sp-dragger{background:transparent;box-shadow:0 0 0 1px #111}.gjs-editor-cont .sp-button-container{float:none;width:100%;position:relative;text-align:right}.gjs-editor-cont .sp-container button,.gjs-editor-cont .sp-container button:hover,.gjs-editor-cont .sp-container button:active{background:rgba(0,0,0,0.2);border-color:rgba(0,0,0,0.2);color:#ddd;text-shadow:none;box-shadow:none;padding:3px 5px}.gjs-editor-cont .sp-palette-container{border:none;float:none;margin:0;padding:5px 10px 0}.gjs-editor-cont .sp-palette .sp-thumb-el,.gjs-editor-cont .sp-palette .sp-thumb-el:hover{border:1px solid rgba(0,0,0,0.9)}.gjs-editor-cont .sp-palette .sp-thumb-el:hover,.gjs-editor-cont .sp-palette .sp-thumb-el.sp-thumb-active{border-color:rgba(0,0,0,0.9)}.gjs-hidden{display:none}@keyframes gjs-slide-down{0%{transform:translate(0, -3rem);opacity:0}100%{transform:translate(0, 0);opacity:1}}@keyframes gjs-slide-up{0%{transform:translate(0, 0);opacity:1}100%{transform:translate(0, -3rem);opacity:0}}.cm-s-hopscotch span.cm-error{color:#ffffff}
+ */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.7.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.7.0") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.7.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.7.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before{content:""}.fa-check-circle:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.CodeMirror{font-family:monospace;height:300px;color:black;direction:ltr}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:white}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:black}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid black;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0 !important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor-mark{background-color:rgba(20,255,20,0.5);-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{0%{}50%{background-color:transparent}100%{}}@-webkit-keyframes blink{0%{}50%{background-color:transparent}100%{}}@keyframes blink{0%{}50%{background-color:transparent}100%{}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:-20px;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:blue}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:bold}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3,.cm-s-default .cm-type{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:#f00}.cm-invalidchar{color:#f00}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,0.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:white}.CodeMirror-scroll{overflow:scroll !important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:none;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none !important;border:none !important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:0.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:none}.CodeMirror-scroll,.CodeMirror-sizer,.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,0.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:none}.cm-s-hopscotch.CodeMirror{background:#322931;color:#d5d3d5}.cm-s-hopscotch div.CodeMirror-selected{background:#433b42 !important}.cm-s-hopscotch .CodeMirror-gutters{background:#322931;border-right:0px}.cm-s-hopscotch .CodeMirror-linenumber{color:#797379}.cm-s-hopscotch .CodeMirror-cursor{border-left:1px solid #989498 !important}.cm-s-hopscotch span.cm-comment{color:#b33508}.cm-s-hopscotch span.cm-atom{color:#c85e7c}.cm-s-hopscotch span.cm-number{color:#c85e7c}.cm-s-hopscotch span.cm-property,.cm-s-hopscotch span.cm-attribute{color:#8fc13e}.cm-s-hopscotch span.cm-keyword{color:#dd464c}.cm-s-hopscotch span.cm-string{color:#fdcc59}.cm-s-hopscotch span.cm-variable{color:#8fc13e}.cm-s-hopscotch span.cm-variable-2{color:#1290bf}.cm-s-hopscotch span.cm-def{color:#fd8b19}.cm-s-hopscotch span.cm-error{background:#dd464c;color:#989498}.cm-s-hopscotch span.cm-bracket{color:#d5d3d5}.cm-s-hopscotch span.cm-tag{color:#dd464c}.cm-s-hopscotch span.cm-link{color:#c85e7c}.cm-s-hopscotch .CodeMirror-matchingbracket{text-decoration:underline;color:white !important}.cm-s-hopscotch .CodeMirror-activeline-background{background:#302020}@font-face{font-family:'font3336';src:url("../fonts/main-fonts.eot?v=20");src:url("../fonts/main-fonts.woff?v=20") format("woff"),url("../fonts/main-fonts.ttf?v=20") format("truetype"),url("../fonts/main-fonts.svg?v=20") format("svg"),url("../fonts/main-fonts.eot?v=20") format("embedded-opentype");font-weight:normal;font-style:normal}.gjs-one-bg{background-color:#444}.gjs-one-color{color:#444}.gjs-one-color-h:hover{color:#444}.gjs-two-bg{background-color:#ddd}.gjs-two-color{color:#ddd}.gjs-two-color-h:hover{color:#ddd}.gjs-three-bg{background-color:#804f7b}.gjs-three-color{color:#804f7b}.gjs-three-color-h:hover{color:#804f7b}.gjs-four-bg{background-color:#d278c9}.gjs-four-color{color:#d278c9}.gjs-four-color-h:hover{color:#d278c9}.gjs-bg-main,.gjs-off-prv,.gjs-sm-sector .gjs-sm-colorp-c,.gjs-clm-tags .gjs-sm-colorp-c{background-color:#444}.gjs-color-main,.gjs-off-prv,.gjs-sm-sector .gjs-sm-stack #gjs-sm-add,.gjs-clm-tags .gjs-sm-stack #gjs-sm-add{color:#ddd;fill:#ddd}.gjs-color-active{color:#f8f8f8;fill:#f8f8f8}.gjs-color-warn{color:#ffca6f;fill:#ffca6f}.gjs-color-hl{color:#71b7f1;fill:#71b7f1}.gjs-fonts::before{display:block;font:normal normal normal 14px font3336;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:5em}.gjs-f-b1::before{content:'Q'}.gjs-f-b2::before{content:'W'}.gjs-f-b3::before{content:'E'}.gjs-f-b37::before{content:'R'}.gjs-f-hero::before{content:'T'}.gjs-f-h1p::before{content:'y'}.gjs-f-3ba::before{content:'u'}.gjs-f-image::before{content:'I'}.gjs-f-text::before{content:'o'}.gjs-f-quo::before{content:'p'}.gjs-f-button::before{content:'B'}.gjs-f-divider::before{content:'D'}.gjs-invis-invis,.gjs-clm-tags #gjs-clm-new,.gjs-no-app{background-color:transparent;border:none;color:inherit}.gjs-no-app{height:10px}.gjs-test::btn{color:'#fff'}.opac50,.gjs-clm-tags #gjs-clm-close{opacity:.5;filter:alpha(opacity=50)}.gjs-checker-bg,.gjs-field-colorp-c,.checker-bg,.gjs-sm-sector .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==")}.gjs-no-user-select,.gjs-grabbing,.gjs-grabbing *,.gjs-layer-name,.gjs-rte-toolbar{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.gjs-no-pointer-events,.gjs-resizer-c,.gjs-margin-v-el,.gjs-padding-v-el,.gjs-fixedmargin-v-el,.gjs-fixedpadding-v-el{pointer-events:none}.gjs-bdrag{pointer-events:none !important;position:absolute !important;z-index:10 !important;width:auto}.gjs-drag-helper{background-color:#3b97e3 !important;pointer-events:none !important;position:absolute !important;z-index:10 !important;transform:scale(0.3) !important;transform-origin:top left !important;-webkit-transform-origin:top left !important;margin:15px !important;transition:none !important;outline:none !important}.gjs-grabbing,.gjs-grabbing *{cursor:grabbing !important;cursor:-webkit-grabbing !important}.gjs-grabbing{overflow:hidden}.gjs-off-prv{position:relative;z-index:10;padding:5px;cursor:pointer}.gjs-editor-cont ::-webkit-scrollbar-track{background:rgba(0,0,0,0.1)}.gjs-editor-cont ::-webkit-scrollbar-thumb{background-color:rgba(255,255,255,0.2)}.gjs-editor-cont ::-webkit-scrollbar{width:8px}.clear{clear:both}.no-select,.gjs-com-no-select,.gjs-com-no-select img,.gjs-category-title,.gjs-sm-sector .gjs-sm-title,.gjs-clm-tags .gjs-sm-title,.gjs-block-category .gjs-title,.gjs-layer-title,.gjs-clm-tags #gjs-clm-close{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.gjs-editor{font-family:Helvetica,sans-serif;font-size:.75rem;position:relative;box-sizing:border-box;height:100%}.gjs-freezed,.gjs-freezed{opacity:.5;filter:alpha(opacity=50);pointer-events:none}.gjs-traits-label{border-bottom:1px solid rgba(0,0,0,0.2);font-weight:lighter;margin-bottom:5px;padding:10px;text-align:left}.gjs-trt-header{font-weight:lighter;padding:10px}.gjs-trt-trait{display:flex;justify-content:start;padding:5px 10px;font-weight:lighter}.gjs-trt-traits{font-size:.75rem}.gjs-trt-trait .gjs-label{width:30%;text-align:left;text-overflow:ellipsis;overflow:hidden}.gjs-trt-trait .gjs-field{width:70%}.gjs-cv-canvas{background-color:rgba(0,0,0,0.15);box-sizing:border-box;width:85%;height:calc(100% - 40px);bottom:0;overflow:hidden;z-index:1;position:absolute;left:0;top:40px}.gjs-cv-canvas .gjs-ghost{display:none;pointer-events:none;background-color:#5b5b5b;border:2px dashed #ccc;position:absolute;z-index:10;opacity:.55;filter:alpha(opacity=55)}.gjs-cv-canvas .gjs-highlighter,.gjs-cv-canvas .gjs-highlighter-sel{position:absolute;outline:1px solid #3b97e3;outline-offset:-1px;pointer-events:none}.gjs-cv-canvas .gjs-highlighter-warning{outline:3px solid #ffca6f}.gjs-cv-canvas .gjs-highlighter-sel{outline:3px solid #3b97e3}.gjs-cv-canvas #gjs-tools{width:100%;position:absolute;top:0;left:0;outline:none}.gjs-cv-canvas>div:first-child{background-color:#fff;position:relative;height:100%;overflow:auto;width:100%}.gjs-cv-canvas *{box-sizing:border-box}.gjs-frame{outline:medium none;height:100%;width:100%;border:none;margin:auto;display:block;transition:width 0.35s ease,height 0.35s ease;position:absolute;top:0;bottom:0;left:0;right:0}.gjs-toolbar{position:absolute;background-color:#3b97e3;color:white;z-index:10}.gjs-toolbar-item{padding:5px 7px;font-size:0.8rem;cursor:pointer}.gjs-resizer-c{position:absolute;left:0;top:0;width:100%;height:100%;z-index:9}.gjs-margin-v-el,.gjs-padding-v-el,.gjs-fixedmargin-v-el,.gjs-fixedpadding-v-el{opacity:.1;filter:alpha(opacity=10);position:absolute;background-color:yellow}.gjs-fixedmargin-v-el,.gjs-fixedpadding-v-el{opacity:.2;filter:alpha(opacity=20)}.gjs-padding-v-el,.gjs-fixedpadding-v-el{background-color:navy}.gjs-resizer-h{pointer-events:all;position:absolute;border:3px solid #3b97e3;width:10px;height:10px;background-color:#fff;margin:-5px}.gjs-resizer-h-tl{top:0;left:0;cursor:nwse-resize}.gjs-resizer-h-tr{top:0;right:0;cursor:nesw-resize}.gjs-resizer-h-tc{top:0;margin:-5px auto;left:0;right:0;cursor:ns-resize}.gjs-resizer-h-cl{left:0;margin:auto -5px;top:0;bottom:0;cursor:ew-resize}.gjs-resizer-h-cr{margin:auto -5px;top:0;bottom:0;right:0;cursor:ew-resize}.gjs-resizer-h-bl{bottom:0;left:0;cursor:nesw-resize}.gjs-resizer-h-bc{bottom:0;margin:-5px auto;left:0;right:0;cursor:ns-resize}.gjs-resizer-h-br{bottom:0;right:0;cursor:nwse-resize}.gjs-pn-panel .gjs-resizer-h{background-color:rgba(0,0,0,0.2);border:none;opacity:0;transition:opacity 0.25s}.gjs-pn-panel .gjs-resizer-h:hover{opacity:1}.gjs-pn-panel .gjs-resizer-h-tc,.gjs-pn-panel .gjs-resizer-h-bc{margin:0 auto;width:100%}.gjs-pn-panel .gjs-resizer-h-cr,.gjs-pn-panel .gjs-resizer-h-cl{margin:auto 0;height:100%}.gjs-resizing .gjs-highlighter,.gjs-resizing .gjs-badge{display:none !important}.gjs-resizing-tl *{cursor:nwse-resize !important}.gjs-resizing-tr *{cursor:nesw-resize !important}.gjs-resizing-tc *{cursor:ns-resize !important}.gjs-resizing-cl *{cursor:ew-resize !important}.gjs-resizing-cr *{cursor:ew-resize !important}.gjs-resizing-bl *{cursor:nesw-resize !important}.gjs-resizing-bc *{cursor:ns-resize !important}.gjs-resizing-br *{cursor:nwse-resize !important}.btn-cl,.gjs-mdl-btn-close,.gjs-am-close{opacity:.3;filter:alpha(opacity=30);font-size:25px;cursor:pointer}.btn-cl:hover,.gjs-mdl-btn-close:hover,.gjs-am-close:hover{opacity:.7;filter:alpha(opacity=70)}.no-dots,.ui-resizable-handle{border:none !important;margin:0 !important;outline:none !important}.gjs-com-dashed *{outline:1px dashed #888;outline-offset:-2px;box-sizing:border-box}.gjs-cv-canvas .gjs-comp-selected{outline:3px solid #3b97e3 !important}*.gjs-com-hover,div.gjs-com-hover{outline:1px solid #3b97e3}*.gjs-com-hover-delete,div.gjs-com-hover-delete{outline:2px solid #dd3636;opacity:.5;filter:alpha(opacity=50)}*.gjs-com-hover-move,div.gjs-com-hover-move{outline:3px solid #ffca6f}.gjs-com-badge,.gjs-com-badge-red,.gjs-badge{pointer-events:none;background-color:#3b97e3;color:#fff;padding:2px 5px;position:absolute;z-index:1;font-size:12px;outline:none;display:none}.gjs-com-badge-red{background-color:#dd3636}.gjs-badge-warning{background-color:#ffca6f}.gjs-placeholder,.gjs-com-placeholder,.gjs-placeholder{position:absolute;z-index:10;pointer-events:none;display:none}.gjs-placeholder,.gjs-placeholder{border-style:solid !important;border-color:#62c462;outline:none;box-sizing:border-box;transition:top .2s,left .2s,width .2s,height .2s}.gjs-placeholder-int,.gjs-com-placeholder-int,.gjs-placeholder-int{background-color:#62c462;box-shadow:0 0 3px rgba(0,0,0,0.2);height:100%;width:100%;pointer-events:none;padding:1.5px;outline:none}.gjs-pn-panel{display:inline-block;position:absolute;box-sizing:border-box;text-align:center;padding:5px;z-index:3}.gjs-pn-panel .icon-undo,.gjs-pn-panel .icon-redo{font-size:20px;height:30px;width:25px}.gjs-pn-commands{width:85%;left:0;top:0;box-shadow:0 0 5px rgba(0,0,0,0.2)}.gjs-pn-options{right:15%;top:0}.gjs-pn-views{border-bottom:2px solid rgba(0,0,0,0.2);right:0;width:15%;z-index:4}.gjs-pn-views-container{height:100%;padding:42px 0 0;right:0;width:15%;overflow:auto;box-shadow:0 0 5px rgba(0,0,0,0.2)}.gjs-pn-buttons{align-items:center;display:flex;justify-content:space-between}.gjs-pn-btn{box-sizing:border-box;min-height:30px;min-width:30px;line-height:21px;background-color:transparent;border:none;font-size:18px;margin-right:5px;border-radius:2px;padding:4px;position:relative;cursor:pointer}.gjs-pn-btn.gjs-pn-active{background-color:rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.25) inset}.gjs-pn-btn svg{fill:currentColor}.gjs-comp-image-placeholder{display:block;background-color:#f5f5f5;color:#777;height:50px;width:50px;line-height:50px;outline:3px solid #ffca6f;outline-offset:-3px;text-align:center;font-size:16.66667px;cursor:pointer}.gjs-comp-image-placeholder.fa-picture-o::after{content:"\f03e"}.gjs-label{line-height:18px}.gjs-fields{display:flex}.gjs-select{padding:0;width:100%}.gjs-select select{padding-right:10px}.gjs-select:-moz-focusring,.gjs-select select:-moz-focusring{color:transparent;text-shadow:0 0 0 rgba(255,255,255,0.7)}.gjs-input:focus,.gjs-button:focus,.gjs-btn-prim:focus,.gjs-select:focus,.gjs-select select:focus{outline:none}.gjs-field input,.gjs-field select,.gjs-field textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;color:inherit;border:none;background-color:transparent;box-sizing:border-box;width:100%;position:relative;padding:5px;z-index:1}.gjs-field input:focus,.gjs-field select:focus,.gjs-field textarea:focus{outline:none}.gjs-field-range{flex:9 1 auto}.gjs-field-integer input{padding-right:30px}.gjs-select option,.gjs-clm-select option,.gjs-sm-select option,.gjs-fields option,.gjs-sm-unit option{background-color:#444;color:#ddd}.gjs-field{background-color:rgba(0,0,0,0.2);border:none;box-shadow:none;border-radius:2px;box-sizing:border-box;padding:0;position:relative}.gjs-field textarea{resize:vertical}.gjs-field .gjs-sel-arrow{height:100%;width:9px;position:absolute;right:0;top:0;z-index:0}.gjs-field .gjs-d-s-arrow{bottom:0;top:0;margin:auto;right:5px;border-top:4px solid rgba(255,255,255,0.7);position:absolute;height:0;width:0;border-left:3px solid transparent;border-right:4px solid transparent;cursor:pointer}.gjs-field-arrows{position:absolute;cursor:ns-resize;margin:auto;height:20px;width:9px;z-index:10;bottom:0;right:3px;top:0}.gjs-field-color,.gjs-field-radio{width:100%}.gjs-field-color input{padding-right:22px;box-sizing:border-box}.gjs-field-colorp{border-left:1px solid rgba(0,0,0,0.2);box-sizing:border-box;height:100%;padding:2px;position:absolute;right:0;top:0;width:22px;z-index:10}.gjs-field-colorp .gjs-checker-bg,.gjs-field-colorp .gjs-field-colorp-c{height:100%;width:100%;border-radius:1px}.gjs-field-colorp-c{height:100%;position:relative;width:100%}.gjs-field-color-picker{background-color:#ddd;cursor:pointer;height:100%;width:100%;box-shadow:0 0 1px rgba(0,0,0,0.2);border-radius:1px;position:absolute;top:0}.gjs-field-checkbox{padding:0;width:auto !important}.gjs-field-checkbox input{display:none}.gjs-field-checkbox input:checked+.gjs-chk-icon{border-color:rgba(255,255,255,0.5);border-width:0 2px 2px 0;border-style:solid}.gjs-radio-item{flex:1 1 auto;text-align:center;border-left:1px solid rgba(0,0,0,0.2)}.gjs-radio-item:first-child{border:none}.gjs-radio-item:hover{background:rgba(0,0,0,0.2)}.gjs-radio-item input{display:none}.gjs-radio-item input:checked+.gjs-radio-item-label{background-color:rgba(255,255,255,0.2)}.gjs-radio-items{display:flex}.gjs-radio-item-label{cursor:pointer;display:block;padding:5px}.gjs-field-units{position:absolute;margin:auto;right:10px;bottom:0;top:0}.gjs-field-unit{position:absolute;right:10px;top:3px;font-size:10px;color:rgba(255,255,255,0.7);cursor:pointer}.gjs-field-arrow-u,.gjs-field-arrow-d{position:absolute;height:0;width:0;border-left:3px solid transparent;border-right:4px solid transparent;border-top:4px solid rgba(255,255,255,0.7);bottom:4px;cursor:pointer}.gjs-field-arrow-u{border-bottom:4px solid rgba(255,255,255,0.7);border-top:none;top:4px}.gjs-field-select{padding:0}.gjs-field-range{background-color:transparent;border:none;box-shadow:none;padding:0}.gjs-field-range input{margin:0;height:100%}.gjs-field-range input:focus{outline:none}.gjs-field-range input::-webkit-slider-thumb{-webkit-appearance:none;margin-top:-4px;height:10px;width:10px;border:1px solid rgba(0,0,0,0.2);border-radius:100%;background-color:#ddd;cursor:pointer}.gjs-field-range input::-moz-range-thumb{height:10px;width:10px;border:1px solid rgba(0,0,0,0.2);border-radius:100%;background-color:#ddd;cursor:pointer}.gjs-field-range input::-ms-thumb{height:10px;width:10px;border:1px solid rgba(0,0,0,0.2);border-radius:100%;background-color:#ddd;cursor:pointer}.gjs-field-range input::-moz-range-track{background-color:rgba(0,0,0,0.2);border-radius:1px;margin-top:3px;height:3px}.gjs-field-range input::-webkit-slider-runnable-track{background-color:rgba(0,0,0,0.2);border-radius:1px;margin-top:3px;height:3px}.gjs-field-range input::-ms-track{background-color:rgba(0,0,0,0.2);border-radius:1px;margin-top:3px;height:3px}.gjs-btn-prim{color:inherit;background-color:rgba(255,255,255,0.1);border-radius:2px;padding:3px 6px;padding:5px;cursor:pointer;border:none}.gjs-btn-prim:active{background-color:rgba(255,255,255,0.1)}.gjs-chk-icon{-ms-transform:rotate(45deg);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);transform:rotate(45deg);box-sizing:border-box;display:block;height:14px;margin:0 5px;width:6px;cursor:pointer}.gjs-add-trasp{background:none;border:none;color:#ddd;cursor:pointer;font-size:1em;border-radius:2px;opacity:.75;filter:alpha(opacity=75)}.gjs-add-trasp:hover{opacity:1;filter:alpha(opacity=100)}.gjs-add-trasp:active{background-color:rgba(0,0,0,0.2)}.gjs-devices-c{display:flex;align-items:center;padding:3px}.gjs-devices-c .gjs-device-label{flex-grow:2;text-align:left;margin-right:10px}.gjs-devices-c .gjs-select{flex-grow:20}.gjs-devices-c .gjs-add-trasp{flex-grow:1;margin-left:5px}.gjs-category-open,.gjs-sm-sector.gjs-sm-open,.gjs-sm-open.gjs-clm-tags,.gjs-block-category.gjs-open{border-bottom:1px solid rgba(0,0,0,0.25)}.gjs-category-title,.gjs-sm-sector .gjs-sm-title,.gjs-clm-tags .gjs-sm-title,.gjs-block-category .gjs-title,.gjs-layer-title{font-weight:lighter;background-color:rgba(0,0,0,0.1);letter-spacing:1px;padding:9px 10px 9px 20px;border-bottom:1px solid rgba(0,0,0,0.25);text-align:left;position:relative;cursor:pointer}.gjs-sm-clear{line-height:0;cursor:pointer}.gjs-sm-header{font-weight:lighter;padding:10px}.gjs-sm-properties{font-size:.75rem;padding:10px 5px;display:flex;flex-wrap:wrap;align-items:flex-end;box-sizing:border-box;width:100%}.gjs-sm-label{margin:5px 5px 3px 0}.gjs-sm-close-btn,.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close{display:block;font-size:23px;position:absolute;cursor:pointer;right:5px;top:0;opacity:.2;filter:alpha(opacity=20)}.gjs-sm-close-btn:hover,.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close:hover,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close:hover{opacity:.7;filter:alpha(opacity=70)}.gjs-sm-sector,.gjs-clm-tags{clear:both;font-weight:lighter;text-align:left}.gjs-sm-sector #gjs-sm-caret,.gjs-clm-tags #gjs-sm-caret{padding-right:5px;font-size:11px}.gjs-sm-sector .gjs-sm-field,.gjs-clm-tags .gjs-sm-field,.gjs-sm-sector .gjs-clm-field,.gjs-clm-tags .gjs-clm-field{width:100%;position:relative}.gjs-sm-sector .gjs-sm-field input,.gjs-clm-tags .gjs-sm-field input,.gjs-sm-sector .gjs-clm-field input,.gjs-clm-tags .gjs-clm-field input,.gjs-sm-sector .gjs-sm-field select,.gjs-clm-tags .gjs-sm-field select,.gjs-sm-sector .gjs-clm-field select,.gjs-clm-tags .gjs-clm-field select{background-color:transparent;color:rgba(255,255,255,0.7);border:none;width:100%}.gjs-sm-sector .gjs-sm-field input,.gjs-clm-tags .gjs-sm-field input,.gjs-sm-sector .gjs-clm-field input,.gjs-clm-tags .gjs-clm-field input{box-sizing:border-box}.gjs-sm-sector .gjs-sm-field select,.gjs-clm-tags .gjs-sm-field select,.gjs-sm-sector .gjs-clm-field select,.gjs-clm-tags .gjs-clm-field select{position:relative;z-index:1;-webkit-appearance:none;-moz-appearance:none;appearance:none}.gjs-sm-sector .gjs-sm-field select::-ms-expand,.gjs-clm-tags .gjs-sm-field select::-ms-expand,.gjs-sm-sector .gjs-clm-field select::-ms-expand,.gjs-clm-tags .gjs-clm-field select::-ms-expand{display:none}.gjs-sm-sector .gjs-sm-field select:-moz-focusring,.gjs-clm-tags .gjs-sm-field select:-moz-focusring,.gjs-sm-sector .gjs-clm-field select:-moz-focusring,.gjs-clm-tags .gjs-clm-field select:-moz-focusring{color:transparent;text-shadow:0 0 0 rgba(255,255,255,0.7)}.gjs-sm-sector .gjs-sm-field input:focus,.gjs-clm-tags .gjs-sm-field input:focus,.gjs-sm-sector .gjs-clm-field input:focus,.gjs-clm-tags .gjs-clm-field input:focus,.gjs-sm-sector .gjs-sm-field select:focus,.gjs-clm-tags .gjs-sm-field select:focus,.gjs-sm-sector .gjs-clm-field select:focus,.gjs-clm-tags .gjs-clm-field select:focus{outline:none}.gjs-sm-sector .gjs-sm-field .gjs-sm-unit,.gjs-clm-tags .gjs-sm-field .gjs-sm-unit,.gjs-sm-sector .gjs-clm-field .gjs-sm-unit,.gjs-clm-tags .gjs-clm-field .gjs-sm-unit{position:absolute;right:10px;top:3px;font-size:10px;color:rgba(255,255,255,0.7);cursor:pointer}.gjs-sm-sector .gjs-sm-field .gjs-clm-sel-arrow,.gjs-clm-tags .gjs-sm-field .gjs-clm-sel-arrow,.gjs-sm-sector .gjs-clm-field .gjs-clm-sel-arrow,.gjs-clm-tags .gjs-clm-field .gjs-clm-sel-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-int-arrows,.gjs-clm-tags .gjs-sm-field .gjs-sm-int-arrows,.gjs-sm-sector .gjs-clm-field .gjs-sm-int-arrows,.gjs-clm-tags .gjs-clm-field .gjs-sm-int-arrows,.gjs-sm-sector .gjs-sm-field .gjs-sm-sel-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-sel-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-sel-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-sel-arrow{height:100%;width:9px;position:absolute;right:0;top:0;cursor:ns-resize}.gjs-sm-sector .gjs-sm-field .gjs-sm-sel-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-sel-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-sel-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-sel-arrow{cursor:pointer}.gjs-sm-sector .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-d-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-d-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-d-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-d-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-d-s-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-u-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-u-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-u-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-u-arrow{position:absolute;height:0;width:0;border-left:3px solid transparent;border-right:4px solid transparent;cursor:pointer}.gjs-sm-sector .gjs-sm-field .gjs-sm-u-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-u-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-u-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-u-arrow{border-bottom:4px solid rgba(255,255,255,0.7);top:4px}.gjs-sm-sector .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-d-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-d-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-d-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-d-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-d-s-arrow{border-top:4px solid rgba(255,255,255,0.7);bottom:4px}.gjs-sm-sector .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-clm-d-s-arrow,.gjs-sm-sector .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-sm-field .gjs-sm-d-s-arrow,.gjs-sm-sector .gjs-clm-field .gjs-sm-d-s-arrow,.gjs-clm-tags .gjs-clm-field .gjs-sm-d-s-arrow{bottom:7px}.gjs-sm-sector .gjs-sm-field.gjs-sm-color,.gjs-clm-tags .gjs-sm-field.gjs-sm-color,.gjs-sm-sector .gjs-sm-color.gjs-clm-field,.gjs-clm-tags .gjs-sm-color.gjs-clm-field,.gjs-sm-sector .gjs-sm-field.gjs-sm-input,.gjs-clm-tags .gjs-sm-field.gjs-sm-input,.gjs-sm-sector .gjs-sm-input.gjs-clm-field,.gjs-clm-tags .gjs-sm-input.gjs-clm-field,.gjs-sm-sector .gjs-sm-field.gjs-sm-integer,.gjs-clm-tags .gjs-sm-field.gjs-sm-integer,.gjs-sm-sector .gjs-sm-integer.gjs-clm-field,.gjs-clm-tags .gjs-sm-integer.gjs-clm-field,.gjs-sm-sector .gjs-sm-field.gjs-sm-list,.gjs-clm-tags .gjs-sm-field.gjs-sm-list,.gjs-sm-sector .gjs-sm-list.gjs-clm-field,.gjs-clm-tags .gjs-sm-list.gjs-clm-field,.gjs-sm-sector .gjs-sm-field.gjs-sm-select,.gjs-clm-tags .gjs-sm-field.gjs-sm-select,.gjs-sm-sector .gjs-sm-select.gjs-clm-field,.gjs-clm-tags .gjs-sm-select.gjs-clm-field,.gjs-sm-sector .gjs-clm-select,.gjs-clm-tags .gjs-clm-select{background-color:rgba(0,0,0,0.2);border:1px solid rgba(0,0,0,0.1);box-shadow:1px 1px 0 rgba(255,255,255,0.1);color:rgba(255,255,255,0.7);border-radius:2px;box-sizing:border-box;padding:0 5px}.gjs-sm-sector .gjs-sm-field.gjs-sm-composite,.gjs-clm-tags .gjs-sm-field.gjs-sm-composite,.gjs-sm-sector .gjs-sm-composite.gjs-clm-field,.gjs-clm-tags .gjs-sm-composite.gjs-clm-field{border-radius:2px}.gjs-sm-sector .gjs-sm-field.gjs-sm-select,.gjs-clm-tags .gjs-sm-field.gjs-sm-select,.gjs-sm-sector .gjs-sm-select.gjs-clm-field,.gjs-clm-tags .gjs-sm-select.gjs-clm-field,.gjs-sm-sector .gjs-clm-select,.gjs-clm-tags .gjs-clm-select{padding:0}.gjs-sm-sector .gjs-sm-field.gjs-sm-select select,.gjs-clm-tags .gjs-sm-field.gjs-sm-select select,.gjs-sm-sector .gjs-sm-select.gjs-clm-field select,.gjs-clm-tags .gjs-sm-select.gjs-clm-field select,.gjs-sm-sector .gjs-clm-select select,.gjs-clm-tags .gjs-clm-select select{height:20px}.gjs-sm-sector .gjs-sm-field.gjs-sm-select option,.gjs-clm-tags .gjs-sm-field.gjs-sm-select option,.gjs-sm-sector .gjs-sm-select.gjs-clm-field option,.gjs-clm-tags .gjs-sm-select.gjs-clm-field option,.gjs-sm-sector .gjs-clm-select option,.gjs-clm-tags .gjs-clm-select option{padding:3px 0}.gjs-sm-sector .gjs-sm-field.gjs-sm-composite,.gjs-clm-tags .gjs-sm-field.gjs-sm-composite,.gjs-sm-sector .gjs-sm-composite.gjs-clm-field,.gjs-clm-tags .gjs-sm-composite.gjs-clm-field{background-color:rgba(0,0,0,0.1);border:1px solid rgba(0,0,0,0.25)}.gjs-sm-sector .gjs-sm-field.gjs-sm-list,.gjs-clm-tags .gjs-sm-field.gjs-sm-list,.gjs-sm-sector .gjs-sm-list.gjs-clm-field,.gjs-clm-tags .gjs-sm-list.gjs-clm-field{width:auto;padding:0;overflow:hidden;float:left}.gjs-sm-sector .gjs-sm-field.gjs-sm-list input,.gjs-clm-tags .gjs-sm-field.gjs-sm-list input,.gjs-sm-sector .gjs-sm-list.gjs-clm-field input,.gjs-clm-tags .gjs-sm-list.gjs-clm-field input{display:none}.gjs-sm-sector .gjs-sm-field.gjs-sm-list label,.gjs-clm-tags .gjs-sm-field.gjs-sm-list label,.gjs-sm-sector .gjs-sm-list.gjs-clm-field label,.gjs-clm-tags .gjs-sm-list.gjs-clm-field label{cursor:pointer;padding:5px;display:block}.gjs-sm-sector .gjs-sm-field.gjs-sm-list .gjs-sm-radio:checked+label,.gjs-clm-tags .gjs-sm-field.gjs-sm-list .gjs-sm-radio:checked+label,.gjs-sm-sector .gjs-sm-list.gjs-clm-field .gjs-sm-radio:checked+label,.gjs-clm-tags .gjs-sm-list.gjs-clm-field .gjs-sm-radio:checked+label{background-color:rgba(255,255,255,0.2)}.gjs-sm-sector .gjs-sm-field.gjs-sm-list .gjs-sm-icon,.gjs-clm-tags .gjs-sm-field.gjs-sm-list .gjs-sm-icon,.gjs-sm-sector .gjs-sm-list.gjs-clm-field .gjs-sm-icon,.gjs-clm-tags .gjs-sm-list.gjs-clm-field .gjs-sm-icon{background-repeat:no-repeat;background-position:center;text-shadow:none;line-height:normal}.gjs-sm-sector .gjs-sm-field.gjs-sm-integer select,.gjs-clm-tags .gjs-sm-field.gjs-sm-integer select,.gjs-sm-sector .gjs-sm-integer.gjs-clm-field select,.gjs-clm-tags .gjs-sm-integer.gjs-clm-field select{width:auto;padding:0}.gjs-sm-sector .gjs-sm-list .gjs-sm-el,.gjs-clm-tags .gjs-sm-list .gjs-sm-el{float:left;border-left:1px solid rgba(0,0,0,0.2)}.gjs-sm-sector .gjs-sm-list .gjs-sm-el:first-child,.gjs-clm-tags .gjs-sm-list .gjs-sm-el:first-child{border:none}.gjs-sm-sector .gjs-sm-list .gjs-sm-el:hover,.gjs-clm-tags .gjs-sm-list .gjs-sm-el:hover{background:rgba(0,0,0,0.2)}.gjs-sm-sector .gjs-sm-slider .gjs-field-integer,.gjs-clm-tags .gjs-sm-slider .gjs-field-integer{flex:1 1 65px}.gjs-sm-sector .gjs-sm-property,.gjs-clm-tags .gjs-sm-property{box-sizing:border-box;float:left;width:50%;margin-bottom:5px;padding:0 5px}.gjs-sm-sector .gjs-sm-property.gjs-sm-composite,.gjs-clm-tags .gjs-sm-property.gjs-sm-composite,.gjs-sm-sector .gjs-sm-property.gjs-sm-file,.gjs-clm-tags .gjs-sm-property.gjs-sm-file,.gjs-sm-sector .gjs-sm-property.gjs-sm-list,.gjs-clm-tags .gjs-sm-property.gjs-sm-list,.gjs-sm-sector .gjs-sm-property.gjs-sm-radio,.gjs-clm-tags .gjs-sm-property.gjs-sm-radio,.gjs-sm-sector .gjs-sm-property.gjs-sm-stack,.gjs-clm-tags .gjs-sm-property.gjs-sm-stack,.gjs-sm-sector .gjs-sm-property.gjs-sm-slider,.gjs-clm-tags .gjs-sm-property.gjs-sm-slider,.gjs-sm-sector .gjs-sm-property.gjs-sm-color,.gjs-clm-tags .gjs-sm-property.gjs-sm-color{width:100%}.gjs-sm-sector .gjs-sm-property .gjs-sm-btn,.gjs-clm-tags .gjs-sm-property .gjs-sm-btn{background-color:rgba(33,33,33,0.2);border-radius:2px;box-shadow:1px 1px 0 rgba(5,5,5,0.2),1px 1px 0 rgba(43,43,43,0.2) inset;padding:5px;position:relative;text-align:center;height:auto;width:100%;cursor:pointer;color:#ddd;box-sizing:border-box;text-shadow:-1px -1px 0 rgba(0,0,0,0.2);border:none;opacity:.85;filter:alpha(opacity=85)}.gjs-sm-sector .gjs-sm-property .gjs-sm-btn-c,.gjs-clm-tags .gjs-sm-property .gjs-sm-btn-c{box-sizing:border-box;float:left;width:100%}.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box{background-color:rgba(255,255,255,0.05);border-radius:2px;margin-top:5px;position:relative;overflow:hidden}.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box.gjs-sm-show,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box.gjs-sm-show{border:1px solid rgba(252,252,252,0.05);padding:3px 20px}.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-box #gjs-sm-close{top:-5px;display:block}.gjs-sm-sector .gjs-sm-property.gjs-sm-file .gjs-sm-show #gjs-sm-preview-file,.gjs-clm-tags .gjs-sm-property.gjs-sm-file .gjs-sm-show #gjs-sm-preview-file{height:50px}.gjs-sm-sector .gjs-sm-property.gjs-sm-file #gjs-sm-preview-file,.gjs-clm-tags .gjs-sm-property.gjs-sm-file #gjs-sm-preview-file{background-size:auto 100%;background-repeat:no-repeat;background-position:center center}.gjs-sm-sector .gjs-sm-property .gjs-sm-layers,.gjs-clm-tags .gjs-sm-property .gjs-sm-layers{margin-top:5px;padding:1px 3px;min-height:30px}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer{background-color:rgba(255,255,255,0.055);border-radius:2px;margin:2px 0;padding:7px;position:relative;cursor:pointer}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box{height:15px;position:absolute;right:27px;top:6px;width:15px}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer #gjs-sm-preview,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer #gjs-sm-preview,.gjs-sm-sector .gjs-sm-property .gjs-sm-layer #gjs-sm-preview-box,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer #gjs-sm-preview-box{border-radius:2px}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer #gjs-sm-close-layer,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer #gjs-sm-close-layer{display:block;font-size:23px;position:absolute;cursor:pointer;right:5px;top:0;opacity:.5;filter:alpha(opacity=50)}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer #gjs-sm-close-layer:hover,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer #gjs-sm-close-layer:hover{opacity:.8;filter:alpha(opacity=80)}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box #gjs-sm-preview,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer>#gjs-sm-preview-box #gjs-sm-preview{background-color:white;height:100%;width:100%;background-size:cover !important}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer.gjs-sm-active,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer.gjs-sm-active{background-color:rgba(255,255,255,0.12)}.gjs-sm-sector .gjs-sm-property .gjs-sm-layer.gjs-sm-no-preview #gjs-sm-preview-box,.gjs-clm-tags .gjs-sm-property .gjs-sm-layer.gjs-sm-no-preview #gjs-sm-preview-box{display:none}.gjs-sm-sector #gjs-sm-text-shadow #gjs-sm-preview::after,.gjs-clm-tags #gjs-sm-text-shadow #gjs-sm-preview::after{color:#000;content:"T";font-weight:900;line-height:17px;padding:0 4px}.gjs-sm-sector .gjs-sm-stack .gjs-sm-properties,.gjs-clm-tags .gjs-sm-stack .gjs-sm-properties{padding:5px 0 0}.gjs-sm-sector .gjs-sm-stack #gjs-sm-add,.gjs-clm-tags .gjs-sm-stack #gjs-sm-add{background:none;border:none;cursor:pointer;outline:none;font-size:22px;line-height:10px;position:absolute;right:0;top:-17px;opacity:0.75}.gjs-sm-sector .gjs-sm-stack #gjs-sm-add:hover,.gjs-clm-tags .gjs-sm-stack #gjs-sm-add:hover{opacity:1;filter:alpha(opacity=100)}.gjs-sm-sector .gjs-sm-colorp-c,.gjs-clm-tags .gjs-sm-colorp-c{height:100%;width:20px;position:absolute;right:0;top:0;box-sizing:border-box;border-radius:2px;padding:2px}.gjs-sm-sector .gjs-sm-colorp-c .gjs-checker-bg,.gjs-clm-tags .gjs-sm-colorp-c .gjs-checker-bg,.gjs-sm-sector .gjs-sm-colorp-c .gjs-field-colorp-c,.gjs-clm-tags .gjs-sm-colorp-c .gjs-field-colorp-c{height:100%;width:100%;border-radius:1px}.gjs-sm-sector .gjs-sm-color-picker,.gjs-clm-tags .gjs-sm-color-picker{background-color:#ddd;cursor:pointer;height:16px;width:100%;margin-top:-16px;box-shadow:0 0 1px rgba(0,0,0,0.2);border-radius:1px}.gjs-sm-sector .gjs-sm-btn-upload #gjs-sm-upload,.gjs-clm-tags .gjs-sm-btn-upload #gjs-sm-upload{left:0;top:0;position:absolute;width:100%;opacity:0;cursor:pointer}.gjs-sm-sector .gjs-sm-btn-upload #gjs-sm-label,.gjs-clm-tags .gjs-sm-btn-upload #gjs-sm-label{padding:2px 0}.gjs-sm-layer>#gjs-sm-move{opacity:.7;filter:alpha(opacity=70);cursor:move;font-size:12px;float:left;margin:0 5px 0 0}.gjs-sm-layer>#gjs-sm-move:hover{opacity:.9;filter:alpha(opacity=90)}.gjs-blocks-c{display:flex;flex-wrap:wrap;justify-content:flex-start}.gjs-block-categories{display:flex;flex-direction:column}.gjs-block-category{width:100%}.gjs-block-category .gjs-caret-icon{margin-right:5px}.gjs-block{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;width:45%;min-width:45px;padding:1em;box-sizing:border-box;min-height:90px;cursor:all-scroll;font-size:11px;font-weight:lighter;text-align:center;display:flex;flex-direction:column;justify-content:space-between;border:1px solid rgba(0,0,0,0.2);border-radius:3px;margin:10px 2.5% 5px;box-shadow:0 1px 0 0 rgba(0,0,0,0.15);transition:all 0.2s ease 0s;transition-property:box-shadow, color}.gjs-block:hover{box-shadow:0 3px 4px 0 rgba(0,0,0,0.15)}.gjs-block svg{fill:currentColor}.gjs-block-svg{width:54px;fill:currentColor}.gjs-block-svg-path{fill:currentColor}.gjs-block.fa{font-size:2em;line-height:2em;padding:11px}.gjs-block-label{line-height:normal;font-size:0.65rem;font-weight:normal;font-family:Helvetica, sans-serif;overflow:hidden;text-overflow:ellipsis}.gjs-block.gjs-bdrag{width:auto;padding:0}.gjs-selected-parent{border:1px solid #ffca6f}.gjs-opac50{opacity:.5;filter:alpha(opacity=50)}.gjs-layer{font-weight:lighter;text-align:left;position:relative;background-color:rgba(0,0,0,0.1);font-size:.75rem}.gjs-layer-hidden{opacity:.55;filter:alpha(opacity=55)}.gjs-layer-count{position:absolute;right:27px;top:9px}.gjs-layer-vis{height:auto !important;width:auto !important;left:0;top:0;padding:7px 5px 7px 10px;position:absolute;cursor:pointer;z-index:1}.gjs-layer-caret{font-size:0.5rem;width:8px;padding:2px;cursor:pointer;position:absolute;left:-9px;top:6px;opacity:.7;filter:alpha(opacity=70)}.gjs-layer-caret:hover{opacity:1;filter:alpha(opacity=100)}.gjs-layer-title{padding:3px 10px 5px 30px;display:flex;align-items:center}.gjs-layer-title-inn{position:relative}.gjs-layer-name{padding:5px;display:inline-block;box-sizing:content-box}.gjs-layer>.gjs-layer-children{display:none}.gjs-layer.open>.gjs-layer-children{display:block}.gjs-layer-no-chld>.gjs-layer-title-inn>.gjs-layer-caret{display:none}.gjs-layer-move{padding:7px 10px 7px 5px;position:absolute;font-size:12px;cursor:move;right:0;top:0}.gjs-layer.gjs-hovered .gjs-layer-title{background-color:rgba(255,255,255,0.015)}.gjs-layer.gjs-selected .gjs-layer-title{background-color:rgba(255,255,255,0.1)}.gjs-layers{position:relative;height:100%}.gjs-layers #gjs-placeholder{width:100%;position:absolute}.gjs-layers #gjs-placeholder #gjs-plh-int{height:100%;padding:1px}.gjs-layers #gjs-placeholder #gjs-plh-int.gjs-insert{background-color:#62c462}.gjs-clm-tags{font-size:.75rem;padding:10px 5px}.gjs-clm-tags #gjs-clm-label,.gjs-clm-tags #gjs-clm-sel{padding:7px 0;float:left}.gjs-clm-tags #gjs-clm-sel{font-style:italic;margin-left:5px}.gjs-clm-tags #gjs-clm-status-c{float:right}.gjs-clm-tags #gjs-clm-tags-field{clear:both;padding:5px;margin-bottom:5px}.gjs-clm-tags #gjs-clm-tags-c{display:inline-block;vertical-align:top}.gjs-clm-tags #gjs-clm-add-tag{background-color:rgba(255,255,255,0.15);border-radius:2px;padding:5px 6px;box-shadow:1px 1px 0 rgba(255,255,255,0.2) inset;border:1px solid rgba(0,0,0,0.15);cursor:pointer}.gjs-clm-tags #gjs-clm-new{color:#ddd;padding:5px 6px;display:none}.gjs-clm-tags .gjs-clm-tag{display:inline-block;border-radius:3px;margin:0 3px 3px 0;padding:5px;cursor:default}.gjs-clm-tags #gjs-clm-close{font-size:20px;line-height:0;cursor:pointer;color:rgba(255,255,255,0.9)}.gjs-clm-tags #gjs-clm-close:hover{opacity:.7;filter:alpha(opacity=70)}.gjs-clm-tags #gjs-clm-checkbox{color:rgba(255,255,255,0.9);vertical-align:middle;cursor:pointer;font-size:9px}.gjs-clm-tags #gjs-clm-tag-label{padding:0 3px;cursor:text}.gjs-mdl-container{font-family:Helvetica,sans-serif;overflow-y:auto;position:fixed;background-color:rgba(0,0,0,0.5);display:flex;top:0;left:0;right:0;bottom:0;z-index:100}.gjs-mdl-dialog{text-shadow:-1px -1px 0 rgba(0,0,0,0.05);animation:gjs-slide-down 0.215s;margin:auto;max-width:850px;width:90%;border-radius:3px;font-weight:lighter;position:relative;z-index:2}.gjs-mdl-title{font-size:1rem}.gjs-mdl-btn-close{position:absolute;right:15px;top:5px}.gjs-mdl-active .gjs-mdl-dialog{animation:gjs-mdl-slide-down 0.216s}.gjs-mdl-header,.gjs-mdl-content{padding:10px 15px;clear:both}.gjs-mdl-header{position:relative;border-bottom:1px solid rgba(0,0,0,0.2);padding:15px 15px 7px}.gjs-export-dl::after{content:"";clear:both;display:block;margin-bottom:10px}.gjs-dropzone{display:none;opacity:0;position:absolute;top:0;left:0;z-index:11;width:100%;height:100%;transition:opacity 0.25s;pointer-events:none}.gjs-dropzone-active .gjs-dropzone{display:block;opacity:1}.gjs-am-assets{height:290px;overflow:auto;clear:both;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:flex-start}.gjs-am-assets-header{padding:5px}.gjs-am-add-asset .gjs-am-add-field{width:70%;float:left}.gjs-am-add-asset button{width:25%;float:right}.gjs-am-preview-cont{position:relative;height:70px;width:30%;background-color:#444;border-radius:2px;float:left;overflow:hidden}.gjs-am-preview{position:absolute;background-position:center center;background-size:cover;background-repeat:no-repeat;height:100%;width:100%;z-index:1}.gjs-am-preview-bg{opacity:.5;filter:alpha(opacity=50);position:absolute;height:100%;width:100%;z-index:0}.gjs-am-dimensions{opacity:.5;filter:alpha(opacity=50);font-size:10px}.gjs-am-meta{width:70%;float:left;font-size:12px;padding:5px 0 0 5px;box-sizing:border-box}.gjs-am-meta>div{margin-bottom:5px}.gjs-am-close{cursor:pointer;position:absolute;right:5px;top:0;display:none}.gjs-am-asset{border-bottom:1px solid rgba(0,0,0,0.2);padding:5px;cursor:pointer;position:relative;box-sizing:border-box;width:100%}.gjs-am-asset:hover .gjs-am-close{display:block}.gjs-am-highlight{background-color:rgba(255,255,255,0.1)}.gjs-am-assets-cont{background-color:rgba(0,0,0,0.1);border-radius:3px;box-sizing:border-box;padding:10px;width:45%;float:right;height:325px;overflow:hidden}.gjs-am-file-uploader{width:55%;float:left}.gjs-am-file-uploader>form{background-color:rgba(0,0,0,0.1);border:2px dashed;border-radius:3px;position:relative;text-align:center;margin-bottom:15px}.gjs-am-file-uploader>form.gjs-am-hover{border:2px solid #62c462;color:#75cb75}.gjs-am-file-uploader>form.gjs-am-disabled{border-color:red}.gjs-am-file-uploader>form #gjs-am-uploadFile{opacity:0;filter:alpha(opacity=0);padding:150px 10px;width:100%;box-sizing:border-box}.gjs-am-file-uploader #gjs-am-title{position:absolute;padding:150px 10px;width:100%}.gjs-cm-editor-c{float:left;box-sizing:border-box;width:50%}.gjs-cm-editor-c .CodeMirror{height:450px}.gjs-cm-editor{font-size:12px}.gjs-cm-editor#gjs-cm-htmlmixed{padding-right:10px;border-right:1px solid rgba(0,0,0,0.2)}.gjs-cm-editor#gjs-cm-htmlmixed #gjs-cm-title{color:#a97d44}.gjs-cm-editor#gjs-cm-css{padding-left:10px}.gjs-cm-editor#gjs-cm-css #gjs-cm-title{color:#ddca7e}.gjs-cm-editor #gjs-cm-title{background-color:rgba(0,0,0,0.2);font-size:12px;padding:5px 10px 3px;text-align:right}.gjs-rte-toolbar{border:1px solid rgba(0,0,0,0.2);position:absolute;border-radius:3px;z-index:10}.gjs-rte-actionbar{display:flex}.gjs-rte-action{display:flex;align-items:center;justify-content:center;padding:5px;min-width:25px;border-right:1px solid rgba(0,0,0,0.2);text-align:center;cursor:pointer;outline:none}.gjs-rte-action:last-child{border-right:none}.gjs-rte-action:hover{background-color:rgba(255,255,255,0.1)}.gjs-rte-active{background-color:rgba(0,0,0,0.2)}.gjs-editor-cont .sp-hue,.gjs-editor-cont .sp-slider{cursor:row-resize}.gjs-editor-cont .sp-color,.gjs-editor-cont .sp-dragger{cursor:crosshair}.gjs-editor-cont .sp-alpha-inner,.gjs-editor-cont .sp-alpha-handle{cursor:col-resize}.gjs-editor-cont .sp-hue{left:90%}.gjs-editor-cont .sp-color{right:15%}.gjs-editor-cont .sp-container{border:1px solid rgba(0,0,0,0.2);box-shadow:0 0 7px rgba(0,0,0,0.2);border-radius:3px}.gjs-editor-cont .sp-picker-container{border:none}.gjs-editor-cont .colpick_dark .colpick_color{outline:1px solid rgba(0,0,0,0.2)}.gjs-editor-cont .sp-cancel,.gjs-editor-cont .sp-cancel:hover{bottom:-8px;color:#777 !important;font-size:25px;left:0;position:absolute;text-decoration:none}.gjs-editor-cont .sp-alpha-handle{background-color:#ccc;border:1px solid #555;width:4px}.gjs-editor-cont .sp-color,.gjs-editor-cont .sp-hue{border:1px solid #333333}.gjs-editor-cont .sp-slider{background-color:#ccc;border:1px solid #555;height:3px;left:-4px;width:22px}.gjs-editor-cont .sp-dragger{background:transparent;box-shadow:0 0 0 1px #111}.gjs-editor-cont .sp-button-container{float:none;width:100%;position:relative;text-align:right}.gjs-editor-cont .sp-container button,.gjs-editor-cont .sp-container button:hover,.gjs-editor-cont .sp-container button:active{background:rgba(0,0,0,0.2);border-color:rgba(0,0,0,0.2);color:#ddd;text-shadow:none;box-shadow:none;padding:3px 5px}.gjs-editor-cont .sp-palette-container{border:none;float:none;margin:0;padding:5px 10px 0}.gjs-editor-cont .sp-palette .sp-thumb-el,.gjs-editor-cont .sp-palette .sp-thumb-el:hover{border:1px solid rgba(0,0,0,0.9)}.gjs-editor-cont .sp-palette .sp-thumb-el:hover,.gjs-editor-cont .sp-palette .sp-thumb-el.sp-thumb-active{border-color:rgba(0,0,0,0.9)}.gjs-hidden{display:none}@keyframes gjs-slide-down{0%{transform:translate(0, -3rem);opacity:0}100%{transform:translate(0, 0);opacity:1}}@keyframes gjs-slide-up{0%{transform:translate(0, 0);opacity:1}100%{transform:translate(0, -3rem);opacity:0}}.cm-s-hopscotch span.cm-error{color:#ffffff}
diff --git a/dist/grapes.js b/dist/grapes.js
index 5619ff8fd..6ee6b0f52 100644
--- a/dist/grapes.js
+++ b/dist/grapes.js
@@ -7,7 +7,7 @@
exports["grapesjs"] = factory();
else
root["grapesjs"] = factory();
-})(typeof self !== 'undefined' ? self : this, function() {
+})(window, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
@@ -46,14 +46,34 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
-/******/ Object.defineProperty(exports, name, {
-/******/ configurable: false,
-/******/ enumerable: true,
-/******/ get: getter
-/******/ });
+/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
+/******/ // define __esModule on exports
+/******/ __webpack_require__.r = function(exports) {
+/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/ }
+/******/ Object.defineProperty(exports, '__esModule', { value: true });
+/******/ };
+/******/
+/******/ // create a fake namespace object
+/******/ // mode & 1: value is a module id, require it
+/******/ // mode & 2: merge all properties of value into the ns
+/******/ // mode & 4: return value when already ns object
+/******/ // mode & 8|1: behave like require
+/******/ __webpack_require__.t = function(value, mode) {
+/******/ if(mode & 1) value = __webpack_require__(value);
+/******/ if(mode & 8) return value;
+/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
+/******/ var ns = Object.create(null);
+/******/ __webpack_require__.r(ns);
+/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
+/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
+/******/ return ns;
+/******/ };
+/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
@@ -69,5635 +89,7315 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
+/******/
/******/ // Load entry module and return exports
-/******/ return __webpack_require__(__webpack_require__.s = 60);
+/******/ return __webpack_require__(__webpack_require__.s = "./src/index.js");
/******/ })
/************************************************************************/
-/******/ ([
-/* 0 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Backbone.js 1.3.3
+/******/ ({
-// (c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
-// Backbone may be freely distributed under the MIT license.
-// For all details and documentation:
-// http://backbonejs.org
+/***/ "./node_modules/backbone-undo/Backbone.Undo.js":
+/*!*****************************************************!*\
+ !*** ./node_modules/backbone-undo/Backbone.Undo.js ***!
+ \*****************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
-(function(factory) {
+var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
+ * Backbone.Undo.js v0.2
+ *
+ * Copyright (c)2013 Oliver Sartun
+ * Released under the MIT License
+ *
+ * Documentation and full license available at
+ * https://github.com/osartun/Backbone.Undo.js
+ */
- // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
- // We use `self` instead of `window` for `WebWorker` support.
- var root = (typeof self == 'object' && self.self === self && self) ||
- (typeof global == 'object' && global.global === global && global);
- // Set up Backbone appropriately for the environment. Start with AMD.
- if (true) {
- !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(1), __webpack_require__(10), exports], __WEBPACK_AMD_DEFINE_RESULT__ = (function(_, $, exports) {
- // Export global even in AMD case in case this script is loaded with
- // others that may still expect a global Backbone.
- root.Backbone = factory(root, exports, _, $);
- }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
+(function (factory) {
+ if (true) {
+ // AMD support
+ !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(/*! underscore */ "./node_modules/backbone-undo/node_modules/underscore/underscore.js"), __webpack_require__(/*! backbone */ "./node_modules/backbone-undo/node_modules/backbone/backbone.js")], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
+ __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
+ (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+ } else {}
+})(function (_, Backbone) {
- // Next for Node.js or CommonJS. jQuery may not be needed as a module.
- } else if (typeof exports !== 'undefined') {
- var _ = require('underscore'), $;
- try { $ = require('jquery'); } catch (e) {}
- factory(root, exports, _, $);
-
- // Finally, as a browser global.
- } else {
- root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
- }
-
-})(function(root, Backbone, _, $) {
-
- // Initial Setup
- // -------------
-
- // Save the previous value of the `Backbone` variable, so that it can be
- // restored later on, if `noConflict` is used.
- var previousBackbone = root.Backbone;
-
- // Create a local reference to a common array method we'll want to use later.
- var slice = Array.prototype.slice;
-
- // Current version of the library. Keep in sync with `package.json`.
- Backbone.VERSION = '1.3.3';
-
- // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
- // the `$` variable.
- Backbone.$ = $;
-
- // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
- // to its previous owner. Returns a reference to this Backbone object.
- Backbone.noConflict = function() {
- root.Backbone = previousBackbone;
- return this;
- };
-
- // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
- // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
- // set a `X-Http-Method-Override` header.
- Backbone.emulateHTTP = false;
-
- // Turn on `emulateJSON` to support legacy servers that can't deal with direct
- // `application/json` requests ... this will encode the body as
- // `application/x-www-form-urlencoded` instead and will send the model in a
- // form param named `model`.
- Backbone.emulateJSON = false;
+ var core_slice = Array.prototype.slice;
- // Proxy Backbone class methods to Underscore functions, wrapping the model's
- // `attributes` object or collection's `models` array behind the scenes.
- //
- // collection.filter(function(model) { return model.get('age') > 10 });
- // collection.each(this.addView);
- //
- // `Function#apply` can be slow so we use the method's arg count, if we know it.
- var addMethod = function(length, method, attribute) {
- switch (length) {
- case 1: return function() {
- return _[method](this[attribute]);
- };
- case 2: return function(value) {
- return _[method](this[attribute], value);
- };
- case 3: return function(iteratee, context) {
- return _[method](this[attribute], cb(iteratee, this), context);
- };
- case 4: return function(iteratee, defaultVal, context) {
- return _[method](this[attribute], cb(iteratee, this), defaultVal, context);
- };
- default: return function() {
- var args = slice.call(arguments);
- args.unshift(this[attribute]);
- return _[method].apply(_, args);
- };
- }
- };
- var addUnderscoreMethods = function(Class, methods, attribute) {
- _.each(methods, function(length, method) {
- if (_[method]) Class.prototype[method] = addMethod(length, method, attribute);
- });
- };
+ /**
+ * As call is faster than apply, this is a faster version of apply as it uses call.
+ *
+ * @param {Function} fn The function to execute
+ * @param {Object} ctx The context the function should be called in
+ * @param {Array} args The array of arguments that should be applied to the function
+ * @return Forwards whatever the called function returns
+ */
+ function apply (fn, ctx, args) {
+ return args.length <= 4 ?
+ fn.call(ctx, args[0], args[1], args[2], args[3]) :
+ fn.apply(ctx, args);
+ }
- // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`.
- var cb = function(iteratee, instance) {
- if (_.isFunction(iteratee)) return iteratee;
- if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee);
- if (_.isString(iteratee)) return function(model) { return model.get(iteratee); };
- return iteratee;
- };
- var modelMatcher = function(attrs) {
- var matcher = _.matches(attrs);
- return function(model) {
- return matcher(model.attributes);
- };
- };
+ /**
+ * Uses slice on an array or an array-like object.
+ *
+ * @param {Array|Object} arr The array or array-like object.
+ * @param {Number} [index] The index from where the array should be sliced. Default is 0.
+ * @return {Array} The sliced array
+ */
+ function slice (arr, index) {
+ return core_slice.call(arr, index);
+ }
- // Backbone.Events
- // ---------------
+ /**
+ * Checks if an object has one or more specific keys. The keys
+ * don't have to be an owned property.
+ * You can call this function either this way:
+ * hasKeys(obj, ["a", "b", "c"])
+ * or this way:
+ * hasKeys(obj, "a", "b", "c")
+ *
+ * @param {Object} obj The object to check on
+ * @param {Array} keys The keys to check for
+ * @return {Boolean} True, if the object has all those keys
+ */
+ function hasKeys (obj, keys) {
+ if (obj == null) return false;
+ if (!_.isArray(keys)) {
+ keys = slice(arguments, 1);
+ }
+ return _.all(keys, function (key) {
+ return key in obj;
+ });
+ }
- // A module that can be mixed in to *any object* in order to provide it with
- // a custom event channel. You may bind a callback to an event with `on` or
- // remove with `off`; `trigger`-ing an event fires all callbacks in
- // succession.
- //
- // var object = {};
- // _.extend(object, Backbone.Events);
- // object.on('expand', function(){ alert('expanded'); });
- // object.trigger('expand');
- //
- var Events = Backbone.Events = {};
+ /**
+ * Returns a number that is unique per call stack. The number gets
+ * changed after the call stack has been completely processed.
+ *
+ * @return {number} MagicFusionIndex
+ */
+ var getMagicFusionIndex = (function () {
+ // If you add several models to a collection or set several
+ // attributes on a model all in sequence and yet all for
+ // example in one function, then several Undo-Actions are
+ // generated.
+ // If you want to undo your last action only the last model
+ // would be removed from the collection or the last set
+ // attribute would be changed back to its previous value.
+ // To prevent that we have to figure out a way to combine
+ // all those actions that happened "at the same time".
+ // Timestamps aren't exact enough. A complex routine could
+ // run several milliseconds and in that time produce a lot
+ // of actions with different timestamps.
+ // Instead we take advantage of the single-threadedness of
+ // JavaScript:
- // Regular expression used to split event strings.
- var eventSplitter = /\s+/;
+ var callstackWasIndexed = false, magicFusionIndex = -1;
+ function indexCycle() {
+ magicFusionIndex++;
+ callstackWasIndexed = true;
+ _.defer(function () {
+ // Here comes the magic. With a Timeout of 0
+ // milliseconds this function gets called whenever
+ // the current callstack is completed
+ callstackWasIndexed = false;
+ })
+ }
+ return function () {
+ if (!callstackWasIndexed) {
+ indexCycle();
+ }
+ return magicFusionIndex;
+ }
+ })();
- // Iterates over the standard `event, callback` (as well as the fancy multiple
- // space-separated events `"change blur", callback` and jQuery-style event
- // maps `{event: callback}`).
- var eventsApi = function(iteratee, events, name, callback, opts) {
- var i = 0, names;
- if (name && typeof name === 'object') {
- // Handle event maps.
- if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
- for (names = _.keys(name); i < names.length ; i++) {
- events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
- }
- } else if (name && eventSplitter.test(name)) {
- // Handle space-separated event names by delegating them individually.
- for (names = name.split(eventSplitter); i < names.length; i++) {
- events = iteratee(events, names[i], callback, opts);
- }
- } else {
- // Finally, standard events.
- events = iteratee(events, name, callback, opts);
- }
- return events;
- };
+ /**
+ * To prevent binding a listener several times to one
+ * object, we register the objects in an ObjectRegistry
+ *
+ * @constructor
+ */
+ function ObjectRegistry () {
+ // This uses two different ways of storing
+ // objects: In case the object has a cid
+ // (which Backbone objects typically have)
+ // it uses this cid as an index. That way
+ // the Array's length attribute doesn't
+ // change and the object isn't an item
+ // in the array, but an object-property.
+ // Otherwise it's added to the Array as an
+ // item.
+ // That way we can use the fast property-
+ // lookup and only have to fall back to
+ // iterating over the array in case
+ // non-Backbone-objects are registered.
+ this.registeredObjects = [];
+ // To return a list of all registered
+ // objects in the 'get' method we have to
+ // store the objects that have a cid in
+ // an additional array.
+ this.cidIndexes = [];
+ }
+ ObjectRegistry.prototype = {
+ /**
+ * Returns whether the object is already registered in this ObjectRegistry or not.
+ *
+ * @this {ObjectRegistry}
+ * @param {Object} obj The object to check
+ * @return {Boolean} True if the object is already registered
+ */
+ isRegistered: function (obj) {
+ // This is where we get a performance boost
+ // by using the two different ways of storing
+ // objects.
+ return obj && obj.cid ? this.registeredObjects[obj.cid] : _.contains(this.registeredObjects, obj);
+ },
+ /**
+ * Registers an object in this ObjectRegistry.
+ *
+ * @this {ObjectRegistry}
+ * @param {Object} obj The object to register
+ * @return {undefined}
+ */
+ register: function (obj) {
+ if (!this.isRegistered(obj)) {
+ if (obj && obj.cid) {
+ this.registeredObjects[obj.cid] = obj;
+ this.cidIndexes.push(obj.cid);
+ } else {
+ this.registeredObjects.push(obj);
+ }
+ return true;
+ }
+ return false;
+ },
+ /**
+ * Unregisters an object from this ObjectRegistry.
+ *
+ * @this {ObjectRegistry}
+ * @param {Object} obj The object to unregister
+ * @return {undefined}
+ */
+ unregister: function (obj) {
+ if (this.isRegistered(obj)) {
+ if (obj && obj.cid) {
+ delete this.registeredObjects[obj.cid];
+ this.cidIndexes.splice(_.indexOf(this.cidIndexes, obj.cid), 1);
+ } else {
+ var i = _.indexOf(this.registeredObjects, obj);
+ this.registeredObjects.splice(i, 1);
+ }
+ return true;
+ }
+ return false;
+ },
+ /**
+ * Returns an array of all objects that are currently in this ObjectRegistry.
+ *
+ * @return {Array} An array of all the objects which are currently in the ObjectRegistry
+ */
+ get: function () {
+ return (_.map(this.cidIndexes, function (cid) {return this.registeredObjects[cid];}, this)).concat(this.registeredObjects);
+ }
+ }
- // Bind an event to a `callback` function. Passing `"all"` will bind
- // the callback to all events fired.
- Events.on = function(name, callback, context) {
- return internalOn(this, name, callback, context);
- };
+ /**
+ * Binds or unbinds the "all"-listener for one or more objects.
+ *
+ * @param {String} which Either "on" or "off"
+ * @param {Object[]} objects Array of the objects on which the "all"-listener should be bound / unbound to
+ * @param {Function} [fn] The function that should be bound / unbound. Optional in case of "off"
+ * @param {Object} [ctx] The context the function should be called in
+ * @return {undefined}
+ */
+ function onoff(which, objects, fn, ctx) {
+ for (var i = 0, l = objects.length, obj; i < l; i++) {
+ obj = objects[i];
+ if (!obj) continue;
+ if (which === "on") {
+ if (!ctx.objectRegistry.register(obj)) {
+ // register returned false, so obj was already registered
+ continue;
+ }
+ } else {
+ if (!ctx.objectRegistry.unregister(obj)) {
+ // unregister returned false, so obj wasn't registered
+ continue;
+ }
+ }
+ if (_.isFunction(obj[which])) {
+ obj[which]("all", fn, ctx);
+ }
+ }
+ }
- // Guard the `listening` argument from the public API.
- var internalOn = function(obj, name, callback, context, listening) {
- obj._events = eventsApi(onApi, obj._events || {}, name, callback, {
- context: context,
- ctx: obj,
- listening: listening
- });
+ /**
+ * Calls the undo/redo-function for a specific action.
+ *
+ * @param {String} which Either "undo" or "redo"
+ * @param {Object} action The Action's attributes
+ * @return {undefined}
+ */
+ function actionUndoRedo (which, action) {
+ var type = action.type, undoTypes = action.undoTypes, fn = !undoTypes[type] || undoTypes[type][which];
+ if (_.isFunction(fn)) {
+ fn(action.object, action.before, action.after, action.options);
+ }
+ }
- if (listening) {
- var listeners = obj._listeners || (obj._listeners = {});
- listeners[listening.id] = listening;
- }
+ /**
+ * The main undo/redo function.
+ *
+ * @param {String} which Either "undo" or "redo"
+ * @param {UndoManager} manager The UndoManager-instance on which an "undo"/"redo"-Event is triggered afterwards
+ * @param {UndoStack} stack The UndoStack on which we perform
+ * @param {Boolean} magic If true, undoes / redoes all actions with the same magicFusionIndex
+ * @param {Boolean} everything If true, undoes / redoes every action that had been tracked
+ * @return {undefined}
+ */
+ function managerUndoRedo (which, manager, stack, magic, everything) {
+ if (stack.isCurrentlyUndoRedoing ||
+ (which === "undo" && stack.pointer === -1) ||
+ (which === "redo" && stack.pointer === stack.length - 1)) {
+ // We're either currently in an undo- / redo-process or
+ // we reached the end of the stack
+ return;
+ }
+ stack.isCurrentlyUndoRedoing = true;
+ var action, actions, isUndo = which === "undo";
+ if (everything) {
+ // Undo / Redo all steps until you reach the stack's beginning / end
+ actions = isUndo && stack.pointer === stack.length - 1 || // If at the stack's end calling undo
+ !isUndo && stack.pointer === -1 ? // or at the stack's beginning calling redo
+ _.clone(stack.models) : // => Take all the models. Otherwise:
+ core_slice.apply(stack.models, isUndo ? [0, stack.pointer] : [stack.pointer, stack.length - 1]);
+ } else {
+ // Undo / Redo only one step
+ action = stack.at(isUndo ? stack.pointer : stack.pointer + 1);
+ actions = magic ? stack.where({"magicFusionIndex": action.get("magicFusionIndex")}) : [action];
+ }
+
+ stack.pointer += (isUndo ? -1 : 1) * actions.length;
+ while (action = isUndo ? actions.pop() : actions.shift()) {
+ // Here we're calling the Action's undo / redo method
+ action[which]();
+ }
+ stack.isCurrentlyUndoRedoing = false;
- return obj;
- };
+ manager.trigger(which, manager);
+ }
- // Inversion-of-control versions of `on`. Tell *this* object to listen to
- // an event in another object... keeping track of what it's listening to
- // for easier unbinding later.
- Events.listenTo = function(obj, name, callback) {
- if (!obj) return this;
- var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
- var listeningTo = this._listeningTo || (this._listeningTo = {});
- var listening = listeningTo[id];
+ /**
+ * Checks whether an UndoAction should be created or not. Therefore it checks
+ * whether a "condition" property is set in the undoTypes-object of the specific
+ * event type. If not, it returns true. If it's set and a boolean, it returns it.
+ * If it's a function, it returns its result, converting it into a boolean.
+ * Otherwise it returns true.
+ *
+ * @param {Object} undoTypesType The object within the UndoTypes that holds the function for this event type (i.e. "change")
+ * @param {Arguments} args The arguments the "condition" function is called with
+ * @return {Boolean} True, if an UndoAction should be created
+ */
+ function validateUndoActionCreation (undoTypesType, args) {
+ var condition = undoTypesType.condition, type = typeof condition;
+ return type === "function" ? !!apply(condition, undoTypesType, args) :
+ type === "boolean" ? condition : true;
+ }
- // This object is not listening to any other events on `obj` yet.
- // Setup the necessary references to track the listening callbacks.
- if (!listening) {
- var thisId = this._listenId || (this._listenId = _.uniqueId('l'));
- listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0};
- }
+ /**
+ * Adds an Undo-Action to the stack.
+ *
+ * @param {UndoStack} stack The undostack the action should be added to.
+ * @param {String} type The event type (i.e. "change")
+ * @param {Arguments} args The arguments passed to the undoTypes' "on"-handler
+ * @param {OwnedUndoTypes} undoTypes The undoTypes-object which has the "on"-handler
+ * @return {undefined}
+ */
+ function addToStack(stack, type, args, undoTypes) {
+ if (stack.track && !stack.isCurrentlyUndoRedoing && type in undoTypes &&
+ validateUndoActionCreation(undoTypes[type], args)) {
+ // An UndoAction should be created
+ var res = apply(undoTypes[type]["on"], undoTypes[type], args), diff;
+ if (hasKeys(res, "object", "before", "after")) {
+ res.type = type;
+ res.magicFusionIndex = getMagicFusionIndex();
+ res.undoTypes = undoTypes;
+ if (stack.pointer < stack.length - 1) {
+ // New Actions must always be added to the end of the stack.
+ // If the pointer is not pointed to the last action in the
+ // stack, presumably because actions were undone before, then
+ // all following actions must be discarded
+ var diff = stack.length - stack.pointer - 1;
+ while (diff--) {
+ stack.pop();
+ }
+ }
+ stack.pointer = stack.length;
+ stack.add(res);
+ if (stack.length > stack.maximumStackLength) {
+ stack.shift();
+ stack.pointer--;
+ }
+ }
+ }
+ }
- // Bind callbacks on obj, and keep track of them on listening.
- internalOn(obj, name, callback, this, listening);
- return this;
- };
- // The reducing API that adds a callback to the `events` object.
- var onApi = function(events, name, callback, options) {
- if (callback) {
- var handlers = events[name] || (events[name] = []);
+ /**
+ * Predefined UndoTypes object with default handlers for the most common events.
+ * @type {Object}
+ */
+ var UndoTypes = {
+ "add": {
+ "undo": function (collection, ignore, model, options) {
+ // Undo add = remove
+ collection.remove(model, options);
+ },
+ "redo": function (collection, ignore, model, options) {
+ // Redo add = add
+ if (options.index) {
+ options.at = options.index;
+ }
+ collection.add(model, options);
+ },
+ "on": function (model, collection, options) {
+ return {
+ object: collection,
+ before: undefined,
+ after: model,
+ options: _.clone(options)
+ };
+ }
+ },
+ "remove": {
+ "undo": function (collection, model, ignore, options) {
+ if ("index" in options) {
+ options.at = options.index;
+ }
+ collection.add(model, options);
+ },
+ "redo": function (collection, model, ignore, options) {
+ collection.remove(model, options);
+ },
+ "on": function (model, collection, options) {
+ return {
+ object: collection,
+ before: model,
+ after: undefined,
+ options: _.clone(options)
+ };
+ }
+ },
+ "change": {
+ "undo": function (model, before, after, options) {
+ if (_.isEmpty(before)) {
+ _.each(_.keys(after), model.unset, model);
+ } else {
+ model.set(before);
+ if (options && options.unsetData && options.unsetData.before && options.unsetData.before.length) {
+ _.each(options.unsetData.before, model.unset, model);
+ }
+ }
+ },
+ "redo": function (model, before, after, options) {
+ if (_.isEmpty(after)) {
+ _.each(_.keys(before), model.unset, model);
+ } else {
+ model.set(after);
+ if (options && options.unsetData && options.unsetData.after && options.unsetData.after.length) {
+ _.each(options.unsetData.after, model.unset, model);
+ }
+ }
+ },
+ "on": function (model, options) {
+ var
+ afterAttributes = model.changedAttributes(),
+ keysAfter = _.keys(afterAttributes),
+ previousAttributes = _.pick(model.previousAttributes(), keysAfter),
+ keysPrevious = _.keys(previousAttributes),
+ unsetData = (options || (options = {})).unsetData = {
+ after: [],
+ before: []
+ };
+
+ if (keysAfter.length != keysPrevious.length) {
+ // There are new attributes or old attributes have been unset
+ if (keysAfter.length > keysPrevious.length) {
+ // New attributes have been added
+ _.each(keysAfter, function (val) {
+ if (!(val in previousAttributes)) {
+ unsetData.before.push(val);
+ }
+ }, this);
+ } else {
+ // Old attributes have been unset
+ _.each(keysPrevious, function (val) {
+ if (!(val in afterAttributes)) {
+ unsetData.after.push(val);
+ }
+ })
+ }
+ }
+ return {
+ object: model,
+ before: previousAttributes,
+ after: afterAttributes,
+ options: _.clone(options)
+ };
+ }
+ },
+ "reset": {
+ "undo": function (collection, before, after) {
+ collection.reset(before);
+ },
+ "redo": function (collection, before, after) {
+ collection.reset(after);
+ },
+ "on": function (collection, options) {
+ return {
+ object: collection,
+ before: options.previousModels,
+ after: _.clone(collection.models)
+ };
+ }
+ }
+ };
+
+ /**
+ * Every UndoManager instance has an own undoTypes object
+ * which is an instance of OwnedUndoTypes. OwnedUndoTypes'
+ * prototype is the global UndoTypes object. Changes to the
+ * global UndoTypes object take effect on every instance of
+ * UndoManager as the object is its prototype. And yet every
+ * local UndoTypes object can be changed individually.
+ *
+ * @constructor
+ */
+ function OwnedUndoTypes () {}
+ OwnedUndoTypes.prototype = UndoTypes;
+
+ /**
+ * Adds, changes or removes an undo-type from an UndoTypes-object.
+ * You can call it this way:
+ * manipulateUndoType (1, "reset", {"on": function () {}}, undoTypes)
+ * or this way to perform bulk actions:
+ * manipulateUndoType (1, {"reset": {"on": function () {}}}, undoTypes)
+ * In case of removing undo-types you can pass an Array for performing
+ * bulk actions:
+ * manipulateUndoType(2, ["reset", "change"], undoTypes)
+ *
+ * @param {Number} manipType Indicates the kind of action to execute: 0 for add, 1 for change, 2 for remove
+ * @param {String|Object|Array} undoType The type of undoType that should be added/changed/removed. Can be an object / array to perform bulk actions
+ * @param {Object} [fns] Object with the functions to add / change. Is optional in case you passed an object as undoType that contains these functions
+ * @param {OwnedUndoTypes|UndoTypes} undoTypesInstance The undoTypes object to act on
+ * @return {undefined}
+ */
+ function manipulateUndoType (manipType, undoType, fns, undoTypesInstance) {
+ // manipType, passed by the calling function
+ // 0: add
+ // 1: change
+ // 2: remove
+ if (typeof undoType === "object") {
+ // bulk action. Iterate over this data.
+ return _.each(undoType, function (val, key) {
+ if (manipType === 2) { // remove
+ // undoType is an array
+ manipulateUndoType (manipType, val, fns, undoTypesInstance);
+ } else {
+ // undoType is an object
+ manipulateUndoType (manipType, key, val, fns);
+ }
+ })
+ }
+
+ switch (manipType) {
+ case 0: // add
+ if (hasKeys(fns, "undo", "redo", "on") && _.all(_.pick(fns, "undo", "redo", "on"), _.isFunction)) {
+ undoTypesInstance[undoType] = fns;
+ }
+ break;
+ case 1: // change
+ if (undoTypesInstance[undoType] && _.isObject(fns)) {
+ // undoTypeInstance[undoType] may be a prototype's property
+ // So, if we did this _.extend(undoTypeInstance[undoType], fns)
+ // we would extend the object on the prototype which means
+ // that this change would have a global effect
+ // Instead we just want to manipulate this instance. That's why
+ // we're doing this:
+ undoTypesInstance[undoType] = _.extend({}, undoTypesInstance[undoType], fns);
+ }
+ break;
+ case 2: // remove
+ delete undoTypesInstance[undoType];
+ break;
+ }
+ return this;
+ }
+
+ /**
+ * Instantiating "Action" creates the UndoActions that
+ * are collected in an UndoStack. It holds all relevant
+ * data to undo / redo an action and has an undo / redo
+ * method.
+ */
+ var Action = Backbone.Model.extend({
+ defaults: {
+ type: null, // "add", "change", "reset", etc.
+ object: null, // The object on which the action occurred
+ before: null, // The previous values which were changed with this action
+ after: null, // The values after this action
+ magicFusionIndex: null // The magicFusionIndex helps to combine
+ // all actions that occurred "at the same time" to undo/redo them altogether
+ },
+ /**
+ * Undoes this action.
+ * @param {OwnedUndoTypes|UndoTypes} undoTypes The undoTypes object which contains the "undo"-handler that should be used
+ * @return {undefined}
+ */
+ undo: function (undoTypes) {
+ actionUndoRedo("undo", this.attributes);
+ },
+ /**
+ * Redoes this action.
+ * @param {OwnedUndoTypes|UndoTypes} undoTypes The undoTypes object which contains the "redo"-handler that should be used
+ * @return {undefined}
+ */
+ redo: function (undoTypes) {
+ actionUndoRedo("redo", this.attributes);
+ }
+ }),
+ /**
+ * An UndoStack is a collection of UndoActions in
+ * chronological order.
+ */
+ UndoStack = Backbone.Collection.extend({
+ model: Action,
+ pointer: -1, // The pointer indicates the index where we are located within the stack. We start at -1
+ track: false,
+ isCurrentlyUndoRedoing: false,
+ maximumStackLength: Infinity,
+ setMaxLength: function (val) {
+ this.maximumStackLength = val;
+ }
+ }),
+ /**
+ * An instance of UndoManager can keep track of
+ * changes to objects and helps to undo them.
+ */
+ UndoManager = Backbone.Model.extend({
+ defaults: {
+ maximumStackLength: Infinity,
+ track: false
+ },
+ /**
+ * The constructor function.
+ * @param {attr} [attr] Object with parameters. The available parameters are:
+ * - maximumStackLength {number} Set the undo-stack's maximum size
+ * - track {boolean} Start tracking changes right away
+ * @return {undefined}
+ */
+ initialize: function (attr) {
+ this.stack = new UndoStack;
+ this.objectRegistry = new ObjectRegistry();
+ this.undoTypes = new OwnedUndoTypes();
+
+ // sync the maximumStackLength attribute with our stack
+ this.stack.setMaxLength(this.get("maximumStackLength"));
+ this.on("change:maximumStackLength", function (model, value) {
+ this.stack.setMaxLength(value);
+ }, this);
+
+ // Start tracking, if attr.track == true
+ if (attr && attr.track) {
+ this.startTracking();
+ }
+
+ // Register objects passed in the "register" attribute
+ if (attr && attr.register) {
+ if (_.isArray(attr.register) || _.isArguments(attr.register)) {
+ apply(this.register, this, attr.register);
+ } else {
+ this.register(attr.register);
+ }
+ }
+ },
+ /**
+ * Starts tracking. Changes of registered objects won't be processed until you've called this function
+ * @return {undefined}
+ */
+ startTracking: function () {
+ this.set("track", true);
+ this.stack.track = true;
+ },
+ /**
+ * Stops tracking. Afterwards changes of registered objects won't be processed.
+ * @return {undefined}
+ */
+ stopTracking: function () {
+ this.set("track", false);
+ this.stack.track = false;
+ },
+ /**
+ * Return the state of the tracking
+ * @return {boolean}
+ */
+ isTracking: function () {
+ return this.get("track");
+ },
+ /**
+ * This is the "all"-handler which is bound to registered
+ * objects. It creates an UndoAction from the event and adds
+ * it to the stack.
+ *
+ * @param {String} type The event type
+ * @return {undefined}
+ */
+ _addToStack: function (type) {
+ addToStack(this.stack, type, slice(arguments, 1), this.undoTypes);
+ },
+ /**
+ * Registers one or more objects to track their changes.
+ * @param {...Object} obj The object or objects of which changes should be tracked
+ * @return {undefined}
+ */
+ register: function () {
+ onoff("on", arguments, this._addToStack, this);
+ },
+ /**
+ * Unregisters one or more objects.
+ * @param {...Object} obj The object or objects of which changes shouldn't be tracked any longer
+ * @return {undefined}
+ */
+ unregister: function () {
+ onoff("off", arguments, this._addToStack, this);
+ },
+ /**
+ * Unregisters all previously registered objects.
+ * @return {undefined}
+ */
+ unregisterAll: function () {
+ apply(this.unregister, this, this.objectRegistry.get());
+ },
+ /**
+ * Undoes the last action or the last set of actions in case 'magic' is true.
+ * @param {Boolean} [magic] If true, all actions that happened basically at the same time are undone together
+ * @return {undefined}
+ */
+ undo: function (magic) {
+ managerUndoRedo("undo", this, this.stack, magic);
+ },
+
+ /**
+ * Undoes all actions ever tracked by the undo manager
+ * @return {undefined}
+ */
+ undoAll: function () {
+ managerUndoRedo("undo", this, this.stack, false, true);
+ },
+
+ /**
+ * Redoes a previously undone action or a set of actions.
+ * @param {Boolean} [magic] If true, all actions that happened basically at the same time are redone together
+ * @return {undefined}
+ */
+ redo: function (magic) {
+ managerUndoRedo("redo", this, this.stack, magic);
+ },
+
+ /**
+ * Redoes all actions ever tracked by the undo manager
+ * @return {undefined}
+ */
+ redoAll: function () {
+ managerUndoRedo("redo", this, this.stack, false, true);
+ },
+ /**
+ * Checks if there's an action in the stack that can be undone / redone
+ * @param {String} type Either "undo" or "redo"
+ * @return {Boolean} True if there is a set of actions which can be undone / redone
+ */
+ isAvailable: function (type) {
+ var s = this.stack, l = s.length;
+
+ switch (type) {
+ case "undo": return l > 0 && s.pointer > -1;
+ case "redo": return l > 0 && s.pointer < l - 1;
+ default: return false;
+ }
+ },
+ /**
+ * Sets the stack-reference to the stack of another undoManager.
+ * @param {UndoManager} undoManager The undoManager whose stack-reference is set to this stack
+ * @return {undefined}
+ */
+ merge: function (undoManager) {
+ // This sets the stack-reference to the stack of another
+ // undoManager so that the stack of this other undoManager
+ // is used by two different managers.
+ // This enables to set up a main-undoManager and besides it
+ // several others for special, exceptional cases (by using
+ // instance-based custom UndoTypes). Models / collections
+ // which need this special treatment are only registered at
+ // those special undoManagers. Those special ones are then
+ // merged into the main-undoManager to write on its stack.
+ // That way it's easier to manage exceptional cases.
+ var args = _.isArray(undoManager) ? undoManager : slice(arguments), manager;
+ while (manager = args.pop()) {
+ if (manager instanceof UndoManager &&
+ manager.stack instanceof UndoStack) {
+ // set the stack reference to our stack
+ manager.stack = this.stack;
+ }
+ }
+ },
+ /**
+ * Add an UndoType to this specific UndoManager-instance.
+ * @param {String} type The event this UndoType is made for
+ * @param {Object} fns An object of functions that are called to generate the data for an UndoAction or to process it. Must have the properties "undo", "redo" and "on". Can have the property "condition".
+ * @return {undefined}
+ */
+ addUndoType: function (type, fns) {
+ manipulateUndoType(0, type, fns, this.undoTypes);
+ },
+ /**
+ * Overwrite properties of an existing UndoType for this specific UndoManager-instance.
+ * @param {String} type The event the UndoType is made for
+ * @param {Object} fns An object of functions that are called to generate the data for an UndoAction or to process it. It extends the existing object.
+ * @return {undefined}
+ */
+ changeUndoType: function (type, fns) {
+ manipulateUndoType(1, type, fns, this.undoTypes);
+ },
+ /**
+ * Remove one or more UndoTypes of this specific UndoManager-instance to fall back to the global UndoTypes.
+ * @param {String|Array} type The event the UndoType that should be removed is made for. You can also pass an array of events.
+ * @return {undefined}
+ */
+ removeUndoType: function (type) {
+ manipulateUndoType(2, type, undefined, this.undoTypes);
+ },
+
+ /**
+ * Removes all actions from the stack.
+ * @return {undefined}
+ */
+ clear: function() {
+ this.stack.reset();
+ this.stack.pointer = -1;
+ }
+ });
+
+ _.extend(UndoManager, {
+ /**
+ * Change the UndoManager's default attributes
+ * @param {Object} defaultAttributes An object with the new default values.
+ * @return {undefined}
+ */
+ defaults: function (defaultAttributes) {
+ _.extend(UndoManager.prototype.defaults, defaultAttributes);
+ },
+ /**
+ * Add an UndoType to the global UndoTypes-object.
+ * @param {String} type The event this UndoType is made for
+ * @param {Object} fns An object of functions that are called to generate the data for an UndoAction or to process it. Must have the properties "undo", "redo" and "on". Can have the property "condition".
+ * @return {undefined}
+ */
+ "addUndoType": function (type, fns) {
+ manipulateUndoType(0, type, fns, UndoTypes);
+ },
+ /**
+ * Overwrite properties of an existing UndoType in the global UndoTypes-object.
+ * @param {String} type The event the UndoType is made for
+ * @param {Object} fns An object of functions that are called to generate the data for an UndoAction or to process it. It extends the existing object.
+ * @return {undefined}
+ */
+ "changeUndoType": function (type, fns) {
+ manipulateUndoType(1, type, fns, UndoTypes)
+ },
+ /**
+ * Remove one or more UndoTypes of this specific UndoManager-instance to fall back to the global UndoTypes.
+ * @param {String|Array} type The event the UndoType that should be removed is made for. You can also pass an array of events.
+ * @return {undefined}
+ */
+ "removeUndoType": function (type) {
+ manipulateUndoType(2, type, undefined, UndoTypes);
+ }
+ })
+
+ return Backbone.UndoManager = UndoManager;
+
+});
+
+
+/***/ }),
+
+/***/ "./node_modules/backbone-undo/node_modules/backbone/backbone.js":
+/*!**********************************************************************!*\
+ !*** ./node_modules/backbone-undo/node_modules/backbone/backbone.js ***!
+ \**********************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Backbone.js 1.2.1
+
+// (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+// Backbone may be freely distributed under the MIT license.
+// For all details and documentation:
+// http://backbonejs.org
+
+(function(factory) {
+
+ // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
+ // We use `self` instead of `window` for `WebWorker` support.
+ var root = (typeof self == 'object' && self.self == self && self) ||
+ (typeof global == 'object' && global.global == global && global);
+
+ // Set up Backbone appropriately for the environment. Start with AMD.
+ if (true) {
+ !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(/*! underscore */ "./node_modules/backbone-undo/node_modules/underscore/underscore.js"), __webpack_require__(/*! jquery */ "./node_modules/cash-dom/dist/cash.js"), exports], __WEBPACK_AMD_DEFINE_RESULT__ = (function(_, $, exports) {
+ // Export global even in AMD case in case this script is loaded with
+ // others that may still expect a global Backbone.
+ root.Backbone = factory(root, exports, _, $);
+ }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
+ __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+
+ // Next for Node.js or CommonJS. jQuery may not be needed as a module.
+ } else { var _, $; }
+
+}(function(root, Backbone, _, $) {
+
+ // Initial Setup
+ // -------------
+
+ // Save the previous value of the `Backbone` variable, so that it can be
+ // restored later on, if `noConflict` is used.
+ var previousBackbone = root.Backbone;
+
+ // Create a local reference to a common array method we'll want to use later.
+ var slice = [].slice;
+
+ // Current version of the library. Keep in sync with `package.json`.
+ Backbone.VERSION = '1.2.1';
+
+ // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
+ // the `$` variable.
+ Backbone.$ = $;
+
+ // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
+ // to its previous owner. Returns a reference to this Backbone object.
+ Backbone.noConflict = function() {
+ root.Backbone = previousBackbone;
+ return this;
+ };
+
+ // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
+ // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
+ // set a `X-Http-Method-Override` header.
+ Backbone.emulateHTTP = false;
+
+ // Turn on `emulateJSON` to support legacy servers that can't deal with direct
+ // `application/json` requests ... this will encode the body as
+ // `application/x-www-form-urlencoded` instead and will send the model in a
+ // form param named `model`.
+ Backbone.emulateJSON = false;
+
+ // Proxy Underscore methods to a Backbone class' prototype using a
+ // particular attribute as the data argument
+ var addMethod = function(length, method, attribute) {
+ switch (length) {
+ case 1: return function() {
+ return _[method](this[attribute]);
+ };
+ case 2: return function(value) {
+ return _[method](this[attribute], value);
+ };
+ case 3: return function(iteratee, context) {
+ return _[method](this[attribute], iteratee, context);
+ };
+ case 4: return function(iteratee, defaultVal, context) {
+ return _[method](this[attribute], iteratee, defaultVal, context);
+ };
+ default: return function() {
+ var args = slice.call(arguments);
+ args.unshift(this[attribute]);
+ return _[method].apply(_, args);
+ };
+ }
+ };
+ var addUnderscoreMethods = function(Class, methods, attribute) {
+ _.each(methods, function(length, method) {
+ if (_[method]) Class.prototype[method] = addMethod(length, method, attribute);
+ });
+ };
+
+ // Backbone.Events
+ // ---------------
+
+ // A module that can be mixed in to *any object* in order to provide it with
+ // custom events. You may bind with `on` or remove with `off` callback
+ // functions to an event; `trigger`-ing an event fires all callbacks in
+ // succession.
+ //
+ // var object = {};
+ // _.extend(object, Backbone.Events);
+ // object.on('expand', function(){ alert('expanded'); });
+ // object.trigger('expand');
+ //
+ var Events = Backbone.Events = {};
+
+ // Regular expression used to split event strings.
+ var eventSplitter = /\s+/;
+
+ // Iterates over the standard `event, callback` (as well as the fancy multiple
+ // space-separated events `"change blur", callback` and jQuery-style event
+ // maps `{event: callback}`), reducing them by manipulating `memo`.
+ // Passes a normalized single event name and callback, as well as any
+ // optional `opts`.
+ var eventsApi = function(iteratee, memo, name, callback, opts) {
+ var i = 0, names;
+ if (name && typeof name === 'object') {
+ // Handle event maps.
+ if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
+ for (names = _.keys(name); i < names.length ; i++) {
+ memo = iteratee(memo, names[i], name[names[i]], opts);
+ }
+ } else if (name && eventSplitter.test(name)) {
+ // Handle space separated event names.
+ for (names = name.split(eventSplitter); i < names.length; i++) {
+ memo = iteratee(memo, names[i], callback, opts);
+ }
+ } else {
+ memo = iteratee(memo, name, callback, opts);
+ }
+ return memo;
+ };
+
+ // Bind an event to a `callback` function. Passing `"all"` will bind
+ // the callback to all events fired.
+ Events.on = function(name, callback, context) {
+ return internalOn(this, name, callback, context);
+ };
+
+ // An internal use `on` function, used to guard the `listening` argument from
+ // the public API.
+ var internalOn = function(obj, name, callback, context, listening) {
+ obj._events = eventsApi(onApi, obj._events || {}, name, callback, {
+ context: context,
+ ctx: obj,
+ listening: listening
+ });
+
+ if (listening) {
+ var listeners = obj._listeners || (obj._listeners = {});
+ listeners[listening.id] = listening;
+ }
+
+ return obj;
+ };
+
+ // Inversion-of-control versions of `on`. Tell *this* object to listen to
+ // an event in another object... keeping track of what it's listening to.
+ Events.listenTo = function(obj, name, callback) {
+ if (!obj) return this;
+ var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
+ var listeningTo = this._listeningTo || (this._listeningTo = {});
+ var listening = listeningTo[id];
+
+ // This object is not listening to any other events on `obj` yet.
+ // Setup the necessary references to track the listening callbacks.
+ if (!listening) {
+ var thisId = this._listenId || (this._listenId = _.uniqueId('l'));
+ listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0};
+ }
+
+ // Bind callbacks on obj, and keep track of them on listening.
+ internalOn(obj, name, callback, this, listening);
+ return this;
+ };
+
+ // The reducing API that adds a callback to the `events` object.
+ var onApi = function(events, name, callback, options) {
+ if (callback) {
+ var handlers = events[name] || (events[name] = []);
var context = options.context, ctx = options.ctx, listening = options.listening;
if (listening) listening.count++;
- handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening});
- }
- return events;
- };
+ handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening });
+ }
+ return events;
+ };
+
+ // Remove one or many callbacks. If `context` is null, removes all
+ // callbacks with that function. If `callback` is null, removes all
+ // callbacks for the event. If `name` is null, removes all bound
+ // callbacks for all events.
+ Events.off = function(name, callback, context) {
+ if (!this._events) return this;
+ this._events = eventsApi(offApi, this._events, name, callback, {
+ context: context,
+ listeners: this._listeners
+ });
+ return this;
+ };
+
+ // Tell this object to stop listening to either specific events ... or
+ // to every object it's currently listening to.
+ Events.stopListening = function(obj, name, callback) {
+ var listeningTo = this._listeningTo;
+ if (!listeningTo) return this;
+
+ var ids = obj ? [obj._listenId] : _.keys(listeningTo);
+
+ for (var i = 0; i < ids.length; i++) {
+ var listening = listeningTo[ids[i]];
+
+ // If listening doesn't exist, this object is not currently
+ // listening to obj. Break out early.
+ if (!listening) break;
+
+ listening.obj.off(name, callback, this);
+ }
+ if (_.isEmpty(listeningTo)) this._listeningTo = void 0;
+
+ return this;
+ };
+
+ // The reducing API that removes a callback from the `events` object.
+ var offApi = function(events, name, callback, options) {
+ // No events to consider.
+ if (!events) return;
+
+ var i = 0, listening;
+ var context = options.context, listeners = options.listeners;
+
+ // Delete all events listeners and "drop" events.
+ if (!name && !callback && !context) {
+ var ids = _.keys(listeners);
+ for (; i < ids.length; i++) {
+ listening = listeners[ids[i]];
+ delete listeners[listening.id];
+ delete listening.listeningTo[listening.objId];
+ }
+ return;
+ }
+
+ var names = name ? [name] : _.keys(events);
+ for (; i < names.length; i++) {
+ name = names[i];
+ var handlers = events[name];
+
+ // Bail out if there are no events stored.
+ if (!handlers) break;
+
+ // Replace events if there are any remaining. Otherwise, clean up.
+ var remaining = [];
+ for (var j = 0; j < handlers.length; j++) {
+ var handler = handlers[j];
+ if (
+ callback && callback !== handler.callback &&
+ callback !== handler.callback._callback ||
+ context && context !== handler.context
+ ) {
+ remaining.push(handler);
+ } else {
+ listening = handler.listening;
+ if (listening && --listening.count === 0) {
+ delete listeners[listening.id];
+ delete listening.listeningTo[listening.objId];
+ }
+ }
+ }
+
+ // Update tail event if the list has any events. Otherwise, clean up.
+ if (remaining.length) {
+ events[name] = remaining;
+ } else {
+ delete events[name];
+ }
+ }
+ if (_.size(events)) return events;
+ };
+
+ // Bind an event to only be triggered a single time. After the first time
+ // the callback is invoked, it will be removed. When multiple events are
+ // passed in using the space-separated syntax, the event will fire once for every
+ // event you passed in, not once for a combination of all events
+ Events.once = function(name, callback, context) {
+ // Map the event into a `{event: once}` object.
+ var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this));
+ return this.on(events, void 0, context);
+ };
+
+ // Inversion-of-control versions of `once`.
+ Events.listenToOnce = function(obj, name, callback) {
+ // Map the event into a `{event: once}` object.
+ var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj));
+ return this.listenTo(obj, events);
+ };
+
+ // Reduces the event callbacks into a map of `{event: onceWrapper}`.
+ // `offer` unbinds the `onceWrapper` after it has been called.
+ var onceMap = function(map, name, callback, offer) {
+ if (callback) {
+ var once = map[name] = _.once(function() {
+ offer(name, once);
+ callback.apply(this, arguments);
+ });
+ once._callback = callback;
+ }
+ return map;
+ };
+
+ // Trigger one or many events, firing all bound callbacks. Callbacks are
+ // passed the same arguments as `trigger` is, apart from the event name
+ // (unless you're listening on `"all"`, which will cause your callback to
+ // receive the true name of the event as the first argument).
+ Events.trigger = function(name) {
+ if (!this._events) return this;
+
+ var length = Math.max(0, arguments.length - 1);
+ var args = Array(length);
+ for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
+
+ eventsApi(triggerApi, this._events, name, void 0, args);
+ return this;
+ };
+
+ // Handles triggering the appropriate event callbacks.
+ var triggerApi = function(objEvents, name, cb, args) {
+ if (objEvents) {
+ var events = objEvents[name];
+ var allEvents = objEvents.all;
+ if (events && allEvents) allEvents = allEvents.slice();
+ if (events) triggerEvents(events, args);
+ if (allEvents) triggerEvents(allEvents, [name].concat(args));
+ }
+ return objEvents;
+ };
+
+ // A difficult-to-believe, but optimized internal dispatch function for
+ // triggering events. Tries to keep the usual cases speedy (most internal
+ // Backbone events have 3 arguments).
+ var triggerEvents = function(events, args) {
+ var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
+ switch (args.length) {
+ case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
+ case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
+ case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
+ case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
+ default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
+ }
+ };
+
+ // Aliases for backwards compatibility.
+ Events.bind = Events.on;
+ Events.unbind = Events.off;
+
+ // Allow the `Backbone` object to serve as a global event bus, for folks who
+ // want global "pubsub" in a convenient place.
+ _.extend(Backbone, Events);
+
+ // Backbone.Model
+ // --------------
+
+ // Backbone **Models** are the basic data object in the framework --
+ // frequently representing a row in a table in a database on your server.
+ // A discrete chunk of data and a bunch of useful, related methods for
+ // performing computations and transformations on that data.
+
+ // Create a new model with the specified attributes. A client id (`cid`)
+ // is automatically generated and assigned for you.
+ var Model = Backbone.Model = function(attributes, options) {
+ var attrs = attributes || {};
+ options || (options = {});
+ this.cid = _.uniqueId(this.cidPrefix);
+ this.attributes = {};
+ if (options.collection) this.collection = options.collection;
+ if (options.parse) attrs = this.parse(attrs, options) || {};
+ attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
+ this.set(attrs, options);
+ this.changed = {};
+ this.initialize.apply(this, arguments);
+ };
+
+ // Attach all inheritable methods to the Model prototype.
+ _.extend(Model.prototype, Events, {
+
+ // A hash of attributes whose current and previous value differ.
+ changed: null,
+
+ // The value returned during the last failed validation.
+ validationError: null,
+
+ // The default name for the JSON `id` attribute is `"id"`. MongoDB and
+ // CouchDB users may want to set this to `"_id"`.
+ idAttribute: 'id',
+
+ // The prefix is used to create the client id which is used to identify models locally.
+ // You may want to override this if you're experiencing name clashes with model ids.
+ cidPrefix: 'c',
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize: function(){},
+
+ // Return a copy of the model's `attributes` object.
+ toJSON: function(options) {
+ return _.clone(this.attributes);
+ },
+
+ // Proxy `Backbone.sync` by default -- but override this if you need
+ // custom syncing semantics for *this* particular model.
+ sync: function() {
+ return Backbone.sync.apply(this, arguments);
+ },
+
+ // Get the value of an attribute.
+ get: function(attr) {
+ return this.attributes[attr];
+ },
+
+ // Get the HTML-escaped value of an attribute.
+ escape: function(attr) {
+ return _.escape(this.get(attr));
+ },
+
+ // Returns `true` if the attribute contains a value that is not null
+ // or undefined.
+ has: function(attr) {
+ return this.get(attr) != null;
+ },
+
+ // Special-cased proxy to underscore's `_.matches` method.
+ matches: function(attrs) {
+ return !!_.iteratee(attrs, this)(this.attributes);
+ },
+
+ // Set a hash of model attributes on the object, firing `"change"`. This is
+ // the core primitive operation of a model, updating the data and notifying
+ // anyone who needs to know about the change in state. The heart of the beast.
+ set: function(key, val, options) {
+ if (key == null) return this;
+
+ // Handle both `"key", value` and `{key: value}` -style arguments.
+ var attrs;
+ if (typeof key === 'object') {
+ attrs = key;
+ options = val;
+ } else {
+ (attrs = {})[key] = val;
+ }
+
+ options || (options = {});
+
+ // Run validation.
+ if (!this._validate(attrs, options)) return false;
+
+ // Extract attributes and options.
+ var unset = options.unset;
+ var silent = options.silent;
+ var changes = [];
+ var changing = this._changing;
+ this._changing = true;
+
+ if (!changing) {
+ this._previousAttributes = _.clone(this.attributes);
+ this.changed = {};
+ }
+
+ var current = this.attributes;
+ var changed = this.changed;
+ var prev = this._previousAttributes;
+
+ // Check for changes of `id`.
+ if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
+
+ // For each `set` attribute, update or delete the current value.
+ for (var attr in attrs) {
+ val = attrs[attr];
+ if (!_.isEqual(current[attr], val)) changes.push(attr);
+ if (!_.isEqual(prev[attr], val)) {
+ changed[attr] = val;
+ } else {
+ delete changed[attr];
+ }
+ unset ? delete current[attr] : current[attr] = val;
+ }
+
+ // Trigger all relevant attribute changes.
+ if (!silent) {
+ if (changes.length) this._pending = options;
+ for (var i = 0; i < changes.length; i++) {
+ this.trigger('change:' + changes[i], this, current[changes[i]], options);
+ }
+ }
+
+ // You might be wondering why there's a `while` loop here. Changes can
+ // be recursively nested within `"change"` events.
+ if (changing) return this;
+ if (!silent) {
+ while (this._pending) {
+ options = this._pending;
+ this._pending = false;
+ this.trigger('change', this, options);
+ }
+ }
+ this._pending = false;
+ this._changing = false;
+ return this;
+ },
+
+ // Remove an attribute from the model, firing `"change"`. `unset` is a noop
+ // if the attribute doesn't exist.
+ unset: function(attr, options) {
+ return this.set(attr, void 0, _.extend({}, options, {unset: true}));
+ },
+
+ // Clear all attributes on the model, firing `"change"`.
+ clear: function(options) {
+ var attrs = {};
+ for (var key in this.attributes) attrs[key] = void 0;
+ return this.set(attrs, _.extend({}, options, {unset: true}));
+ },
+
+ // Determine if the model has changed since the last `"change"` event.
+ // If you specify an attribute name, determine if that attribute has changed.
+ hasChanged: function(attr) {
+ if (attr == null) return !_.isEmpty(this.changed);
+ return _.has(this.changed, attr);
+ },
+
+ // Return an object containing all the attributes that have changed, or
+ // false if there are no changed attributes. Useful for determining what
+ // parts of a view need to be updated and/or what attributes need to be
+ // persisted to the server. Unset attributes will be set to undefined.
+ // You can also pass an attributes object to diff against the model,
+ // determining if there *would be* a change.
+ changedAttributes: function(diff) {
+ if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
+ var old = this._changing ? this._previousAttributes : this.attributes;
+ var changed = {};
+ for (var attr in diff) {
+ var val = diff[attr];
+ if (_.isEqual(old[attr], val)) continue;
+ changed[attr] = val;
+ }
+ return _.size(changed) ? changed : false;
+ },
+
+ // Get the previous value of an attribute, recorded at the time the last
+ // `"change"` event was fired.
+ previous: function(attr) {
+ if (attr == null || !this._previousAttributes) return null;
+ return this._previousAttributes[attr];
+ },
+
+ // Get all of the attributes of the model at the time of the previous
+ // `"change"` event.
+ previousAttributes: function() {
+ return _.clone(this._previousAttributes);
+ },
+
+ // Fetch the model from the server, merging the response with the model's
+ // local attributes. Any changed attributes will trigger a "change" event.
+ fetch: function(options) {
+ options = _.extend({parse: true}, options);
+ var model = this;
+ var success = options.success;
+ options.success = function(resp) {
+ var serverAttrs = options.parse ? model.parse(resp, options) : resp;
+ if (!model.set(serverAttrs, options)) return false;
+ if (success) success.call(options.context, model, resp, options);
+ model.trigger('sync', model, resp, options);
+ };
+ wrapError(this, options);
+ return this.sync('read', this, options);
+ },
+
+ // Set a hash of model attributes, and sync the model to the server.
+ // If the server returns an attributes hash that differs, the model's
+ // state will be `set` again.
+ save: function(key, val, options) {
+ // Handle both `"key", value` and `{key: value}` -style arguments.
+ var attrs;
+ if (key == null || typeof key === 'object') {
+ attrs = key;
+ options = val;
+ } else {
+ (attrs = {})[key] = val;
+ }
+
+ options = _.extend({validate: true, parse: true}, options);
+ var wait = options.wait;
+
+ // If we're not waiting and attributes exist, save acts as
+ // `set(attr).save(null, opts)` with validation. Otherwise, check if
+ // the model will be valid when the attributes, if any, are set.
+ if (attrs && !wait) {
+ if (!this.set(attrs, options)) return false;
+ } else {
+ if (!this._validate(attrs, options)) return false;
+ }
+
+ // After a successful server-side save, the client is (optionally)
+ // updated with the server-side state.
+ var model = this;
+ var success = options.success;
+ var attributes = this.attributes;
+ options.success = function(resp) {
+ // Ensure attributes are restored during synchronous saves.
+ model.attributes = attributes;
+ var serverAttrs = options.parse ? model.parse(resp, options) : resp;
+ if (wait) serverAttrs = _.extend({}, attrs, serverAttrs);
+ if (serverAttrs && !model.set(serverAttrs, options)) return false;
+ if (success) success.call(options.context, model, resp, options);
+ model.trigger('sync', model, resp, options);
+ };
+ wrapError(this, options);
+
+ // Set temporary attributes if `{wait: true}` to properly find new ids.
+ if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
+
+ var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
+ if (method === 'patch' && !options.attrs) options.attrs = attrs;
+ var xhr = this.sync(method, this, options);
+
+ // Restore attributes.
+ this.attributes = attributes;
+
+ return xhr;
+ },
+
+ // Destroy this model on the server if it was already persisted.
+ // Optimistically removes the model from its collection, if it has one.
+ // If `wait: true` is passed, waits for the server to respond before removal.
+ destroy: function(options) {
+ options = options ? _.clone(options) : {};
+ var model = this;
+ var success = options.success;
+ var wait = options.wait;
+
+ var destroy = function() {
+ model.stopListening();
+ model.trigger('destroy', model, model.collection, options);
+ };
+
+ options.success = function(resp) {
+ if (wait) destroy();
+ if (success) success.call(options.context, model, resp, options);
+ if (!model.isNew()) model.trigger('sync', model, resp, options);
+ };
+
+ var xhr = false;
+ if (this.isNew()) {
+ _.defer(options.success);
+ } else {
+ wrapError(this, options);
+ xhr = this.sync('delete', this, options);
+ }
+ if (!wait) destroy();
+ return xhr;
+ },
+
+ // Default URL for the model's representation on the server -- if you're
+ // using Backbone's restful methods, override this to change the endpoint
+ // that will be called.
+ url: function() {
+ var base =
+ _.result(this, 'urlRoot') ||
+ _.result(this.collection, 'url') ||
+ urlError();
+ if (this.isNew()) return base;
+ var id = this.get(this.idAttribute);
+ return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
+ },
+
+ // **parse** converts a response into the hash of attributes to be `set` on
+ // the model. The default implementation is just to pass the response along.
+ parse: function(resp, options) {
+ return resp;
+ },
+
+ // Create a new model with identical attributes to this one.
+ clone: function() {
+ return new this.constructor(this.attributes);
+ },
+
+ // A model is new if it has never been saved to the server, and lacks an id.
+ isNew: function() {
+ return !this.has(this.idAttribute);
+ },
+
+ // Check if the model is currently in a valid state.
+ isValid: function(options) {
+ return this._validate({}, _.defaults({validate: true}, options));
+ },
+
+ // Run validation against the next complete set of model attributes,
+ // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
+ _validate: function(attrs, options) {
+ if (!options.validate || !this.validate) return true;
+ attrs = _.extend({}, this.attributes, attrs);
+ var error = this.validationError = this.validate(attrs, options) || null;
+ if (!error) return true;
+ this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
+ return false;
+ }
+
+ });
+
+ // Underscore methods that we want to implement on the Model.
+ var modelMethods = { keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
+ omit: 0, chain: 1, isEmpty: 1 };
+
+ // Mix in each Underscore method as a proxy to `Model#attributes`.
+ addUnderscoreMethods(Model, modelMethods, 'attributes');
+
+ // Backbone.Collection
+ // -------------------
- // Remove one or many callbacks. If `context` is null, removes all
- // callbacks with that function. If `callback` is null, removes all
- // callbacks for the event. If `name` is null, removes all bound
- // callbacks for all events.
- Events.off = function(name, callback, context) {
- if (!this._events) return this;
- this._events = eventsApi(offApi, this._events, name, callback, {
- context: context,
- listeners: this._listeners
- });
- return this;
+ // If models tend to represent a single row of data, a Backbone Collection is
+ // more analogous to a table full of data ... or a small slice or page of that
+ // table, or a collection of rows that belong together for a particular reason
+ // -- all of the messages in this particular folder, all of the documents
+ // belonging to this particular author, and so on. Collections maintain
+ // indexes of their models, both in order, and for lookup by `id`.
+
+ // Create a new **Collection**, perhaps to contain a specific type of `model`.
+ // If a `comparator` is specified, the Collection will maintain
+ // its models in sort order, as they're added and removed.
+ var Collection = Backbone.Collection = function(models, options) {
+ options || (options = {});
+ if (options.model) this.model = options.model;
+ if (options.comparator !== void 0) this.comparator = options.comparator;
+ this._reset();
+ this.initialize.apply(this, arguments);
+ if (models) this.reset(models, _.extend({silent: true}, options));
};
- // Tell this object to stop listening to either specific events ... or
- // to every object it's currently listening to.
- Events.stopListening = function(obj, name, callback) {
- var listeningTo = this._listeningTo;
- if (!listeningTo) return this;
+ // Default options for `Collection#set`.
+ var setOptions = {add: true, remove: true, merge: true};
+ var addOptions = {add: true, remove: false};
- var ids = obj ? [obj._listenId] : _.keys(listeningTo);
+ // Define the Collection's inheritable methods.
+ _.extend(Collection.prototype, Events, {
- for (var i = 0; i < ids.length; i++) {
- var listening = listeningTo[ids[i]];
+ // The default model for a collection is just a **Backbone.Model**.
+ // This should be overridden in most cases.
+ model: Model,
- // If listening doesn't exist, this object is not currently
- // listening to obj. Break out early.
- if (!listening) break;
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize: function(){},
- listening.obj.off(name, callback, this);
- }
+ // The JSON representation of a Collection is an array of the
+ // models' attributes.
+ toJSON: function(options) {
+ return this.map(function(model) { return model.toJSON(options); });
+ },
- return this;
- };
+ // Proxy `Backbone.sync` by default.
+ sync: function() {
+ return Backbone.sync.apply(this, arguments);
+ },
- // The reducing API that removes a callback from the `events` object.
- var offApi = function(events, name, callback, options) {
- if (!events) return;
+ // Add a model, or list of models to the set.
+ add: function(models, options) {
+ return this.set(models, _.extend({merge: false}, options, addOptions));
+ },
- var i = 0, listening;
- var context = options.context, listeners = options.listeners;
+ // Remove a model, or a list of models from the set.
+ remove: function(models, options) {
+ options = _.extend({}, options);
+ var singular = !_.isArray(models);
+ models = singular ? [models] : _.clone(models);
+ var removed = this._removeModels(models, options);
+ if (!options.silent && removed) this.trigger('update', this, options);
+ return singular ? removed[0] : removed;
+ },
- // Delete all events listeners and "drop" events.
- if (!name && !callback && !context) {
- var ids = _.keys(listeners);
- for (; i < ids.length; i++) {
- listening = listeners[ids[i]];
- delete listeners[listening.id];
- delete listening.listeningTo[listening.objId];
- }
- return;
- }
+ // Update a collection by `set`-ing a new list of models, adding new ones,
+ // removing models that are no longer present, and merging models that
+ // already exist in the collection, as necessary. Similar to **Model#set**,
+ // the core operation for updating the data contained by the collection.
+ set: function(models, options) {
+ options = _.defaults({}, options, setOptions);
+ if (options.parse && !this._isModel(models)) models = this.parse(models, options);
+ var singular = !_.isArray(models);
+ models = singular ? (models ? [models] : []) : models.slice();
+ var id, model, attrs, existing, sort;
+ var at = options.at;
+ if (at != null) at = +at;
+ if (at < 0) at += this.length + 1;
+ var sortable = this.comparator && (at == null) && options.sort !== false;
+ var sortAttr = _.isString(this.comparator) ? this.comparator : null;
+ var toAdd = [], toRemove = [], modelMap = {};
+ var add = options.add, merge = options.merge, remove = options.remove;
+ var order = !sortable && add && remove ? [] : false;
+ var orderChanged = false;
- var names = name ? [name] : _.keys(events);
- for (; i < names.length; i++) {
- name = names[i];
- var handlers = events[name];
+ // Turn bare objects into model references, and prevent invalid models
+ // from being added.
+ for (var i = 0; i < models.length; i++) {
+ attrs = models[i];
- // Bail out if there are no events stored.
- if (!handlers) break;
+ // If a duplicate is found, prevent it from being added and
+ // optionally merge it into the existing model.
+ if (existing = this.get(attrs)) {
+ if (remove) modelMap[existing.cid] = true;
+ if (merge && attrs !== existing) {
+ attrs = this._isModel(attrs) ? attrs.attributes : attrs;
+ if (options.parse) attrs = existing.parse(attrs, options);
+ existing.set(attrs, options);
+ if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
+ }
+ models[i] = existing;
- // Replace events if there are any remaining. Otherwise, clean up.
- var remaining = [];
- for (var j = 0; j < handlers.length; j++) {
- var handler = handlers[j];
- if (
- callback && callback !== handler.callback &&
- callback !== handler.callback._callback ||
- context && context !== handler.context
- ) {
- remaining.push(handler);
+ // If this is a new, valid model, push it to the `toAdd` list.
+ } else if (add) {
+ model = models[i] = this._prepareModel(attrs, options);
+ if (!model) continue;
+ toAdd.push(model);
+ this._addReference(model, options);
+ }
+
+ // Do not add multiple models with the same `id`.
+ model = existing || model;
+ if (!model) continue;
+ id = this.modelId(model.attributes);
+ if (order && (model.isNew() || !modelMap[id])) {
+ order.push(model);
+
+ // Check to see if this is actually a new model at this index.
+ orderChanged = orderChanged || !this.models[i] || model.cid !== this.models[i].cid;
+ }
+
+ modelMap[id] = true;
+ }
+
+ // Remove nonexistent models if appropriate.
+ if (remove) {
+ for (var i = 0; i < this.length; i++) {
+ if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
+ }
+ if (toRemove.length) this._removeModels(toRemove, options);
+ }
+
+ // See if sorting is needed, update `length` and splice in new models.
+ if (toAdd.length || orderChanged) {
+ if (sortable) sort = true;
+ this.length += toAdd.length;
+ if (at != null) {
+ for (var i = 0; i < toAdd.length; i++) {
+ this.models.splice(at + i, 0, toAdd[i]);
+ }
} else {
- listening = handler.listening;
- if (listening && --listening.count === 0) {
- delete listeners[listening.id];
- delete listening.listeningTo[listening.objId];
+ if (order) this.models.length = 0;
+ var orderedModels = order || toAdd;
+ for (var i = 0; i < orderedModels.length; i++) {
+ this.models.push(orderedModels[i]);
}
}
}
- // Update tail event if the list has any events. Otherwise, clean up.
- if (remaining.length) {
- events[name] = remaining;
+ // Silently sort the collection if appropriate.
+ if (sort) this.sort({silent: true});
+
+ // Unless silenced, it's time to fire all appropriate add/sort events.
+ if (!options.silent) {
+ var addOpts = at != null ? _.clone(options) : options;
+ for (var i = 0; i < toAdd.length; i++) {
+ if (at != null) addOpts.index = at + i;
+ (model = toAdd[i]).trigger('add', model, this, addOpts);
+ }
+ if (sort || orderChanged) this.trigger('sort', this, options);
+ if (toAdd.length || toRemove.length) this.trigger('update', this, options);
+ }
+
+ // Return the added (or merged) model (or models).
+ return singular ? models[0] : models;
+ },
+
+ // When you have more items than you want to add or remove individually,
+ // you can reset the entire set with a new list of models, without firing
+ // any granular `add` or `remove` events. Fires `reset` when finished.
+ // Useful for bulk operations and optimizations.
+ reset: function(models, options) {
+ options = options ? _.clone(options) : {};
+ for (var i = 0; i < this.models.length; i++) {
+ this._removeReference(this.models[i], options);
+ }
+ options.previousModels = this.models;
+ this._reset();
+ models = this.add(models, _.extend({silent: true}, options));
+ if (!options.silent) this.trigger('reset', this, options);
+ return models;
+ },
+
+ // Add a model to the end of the collection.
+ push: function(model, options) {
+ return this.add(model, _.extend({at: this.length}, options));
+ },
+
+ // Remove a model from the end of the collection.
+ pop: function(options) {
+ var model = this.at(this.length - 1);
+ return this.remove(model, options);
+ },
+
+ // Add a model to the beginning of the collection.
+ unshift: function(model, options) {
+ return this.add(model, _.extend({at: 0}, options));
+ },
+
+ // Remove a model from the beginning of the collection.
+ shift: function(options) {
+ var model = this.at(0);
+ return this.remove(model, options);
+ },
+
+ // Slice out a sub-array of models from the collection.
+ slice: function() {
+ return slice.apply(this.models, arguments);
+ },
+
+ // Get a model from the set by id.
+ get: function(obj) {
+ if (obj == null) return void 0;
+ var id = this.modelId(this._isModel(obj) ? obj.attributes : obj);
+ return this._byId[obj] || this._byId[id] || this._byId[obj.cid];
+ },
+
+ // Get the model at the given index.
+ at: function(index) {
+ if (index < 0) index += this.length;
+ return this.models[index];
+ },
+
+ // Return models with matching attributes. Useful for simple cases of
+ // `filter`.
+ where: function(attrs, first) {
+ var matches = _.matches(attrs);
+ return this[first ? 'find' : 'filter'](function(model) {
+ return matches(model.attributes);
+ });
+ },
+
+ // Return the first model with matching attributes. Useful for simple cases
+ // of `find`.
+ findWhere: function(attrs) {
+ return this.where(attrs, true);
+ },
+
+ // Force the collection to re-sort itself. You don't need to call this under
+ // normal circumstances, as the set will maintain sort order as each item
+ // is added.
+ sort: function(options) {
+ if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
+ options || (options = {});
+
+ // Run sort based on type of `comparator`.
+ if (_.isString(this.comparator) || this.comparator.length === 1) {
+ this.models = this.sortBy(this.comparator, this);
} else {
- delete events[name];
+ this.models.sort(_.bind(this.comparator, this));
}
- }
- return events;
- };
- // Bind an event to only be triggered a single time. After the first time
- // the callback is invoked, its listener will be removed. If multiple events
- // are passed in using the space-separated syntax, the handler will fire
- // once for each event, not once for a combination of all events.
- Events.once = function(name, callback, context) {
- // Map the event into a `{event: once}` object.
- var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this));
- if (typeof name === 'string' && context == null) callback = void 0;
- return this.on(events, callback, context);
- };
+ if (!options.silent) this.trigger('sort', this, options);
+ return this;
+ },
- // Inversion-of-control versions of `once`.
- Events.listenToOnce = function(obj, name, callback) {
- // Map the event into a `{event: once}` object.
- var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj));
- return this.listenTo(obj, events);
- };
+ // Pluck an attribute from each model in the collection.
+ pluck: function(attr) {
+ return _.invoke(this.models, 'get', attr);
+ },
- // Reduces the event callbacks into a map of `{event: onceWrapper}`.
- // `offer` unbinds the `onceWrapper` after it has been called.
- var onceMap = function(map, name, callback, offer) {
- if (callback) {
- var once = map[name] = _.once(function() {
- offer(name, once);
- callback.apply(this, arguments);
+ // Fetch the default set of models for this collection, resetting the
+ // collection when they arrive. If `reset: true` is passed, the response
+ // data will be passed through the `reset` method instead of `set`.
+ fetch: function(options) {
+ options = _.extend({parse: true}, options);
+ var success = options.success;
+ var collection = this;
+ options.success = function(resp) {
+ var method = options.reset ? 'reset' : 'set';
+ collection[method](resp, options);
+ if (success) success.call(options.context, collection, resp, options);
+ collection.trigger('sync', collection, resp, options);
+ };
+ wrapError(this, options);
+ return this.sync('read', this, options);
+ },
+
+ // Create a new instance of a model in this collection. Add the model to the
+ // collection immediately, unless `wait: true` is passed, in which case we
+ // wait for the server to agree.
+ create: function(model, options) {
+ options = options ? _.clone(options) : {};
+ var wait = options.wait;
+ model = this._prepareModel(model, options);
+ if (!model) return false;
+ if (!wait) this.add(model, options);
+ var collection = this;
+ var success = options.success;
+ options.success = function(model, resp, callbackOpts) {
+ if (wait) collection.add(model, callbackOpts);
+ if (success) success.call(callbackOpts.context, model, resp, callbackOpts);
+ };
+ model.save(null, options);
+ return model;
+ },
+
+ // **parse** converts a response into a list of models to be added to the
+ // collection. The default implementation is just to pass it through.
+ parse: function(resp, options) {
+ return resp;
+ },
+
+ // Create a new collection with an identical list of models as this one.
+ clone: function() {
+ return new this.constructor(this.models, {
+ model: this.model,
+ comparator: this.comparator
});
- once._callback = callback;
- }
- return map;
- };
+ },
- // Trigger one or many events, firing all bound callbacks. Callbacks are
- // passed the same arguments as `trigger` is, apart from the event name
- // (unless you're listening on `"all"`, which will cause your callback to
- // receive the true name of the event as the first argument).
- Events.trigger = function(name) {
- if (!this._events) return this;
+ // Define how to uniquely identify models in the collection.
+ modelId: function (attrs) {
+ return attrs[this.model.prototype.idAttribute || 'id'];
+ },
- var length = Math.max(0, arguments.length - 1);
- var args = Array(length);
- for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
+ // Private method to reset all internal state. Called when the collection
+ // is first initialized or reset.
+ _reset: function() {
+ this.length = 0;
+ this.models = [];
+ this._byId = {};
+ },
- eventsApi(triggerApi, this._events, name, void 0, args);
- return this;
- };
+ // Prepare a hash of attributes (or other model) to be added to this
+ // collection.
+ _prepareModel: function(attrs, options) {
+ if (this._isModel(attrs)) {
+ if (!attrs.collection) attrs.collection = this;
+ return attrs;
+ }
+ options = options ? _.clone(options) : {};
+ options.collection = this;
+ var model = new this.model(attrs, options);
+ if (!model.validationError) return model;
+ this.trigger('invalid', this, model.validationError, options);
+ return false;
+ },
- // Handles triggering the appropriate event callbacks.
- var triggerApi = function(objEvents, name, callback, args) {
- if (objEvents) {
- var events = objEvents[name];
- var allEvents = objEvents.all;
- if (events && allEvents) allEvents = allEvents.slice();
- if (events) triggerEvents(events, args);
- if (allEvents) triggerEvents(allEvents, [name].concat(args));
- }
- return objEvents;
- };
+ // Internal method called by both remove and set.
+ // Returns removed models, or false if nothing is removed.
+ _removeModels: function(models, options) {
+ var removed = [];
+ for (var i = 0; i < models.length; i++) {
+ var model = this.get(models[i]);
+ if (!model) continue;
- // A difficult-to-believe, but optimized internal dispatch function for
- // triggering events. Tries to keep the usual cases speedy (most internal
- // Backbone events have 3 arguments).
- var triggerEvents = function(events, args) {
- var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
- switch (args.length) {
- case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
- case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
- case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
- case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
- default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
+ var index = this.indexOf(model);
+ this.models.splice(index, 1);
+ this.length--;
+
+ if (!options.silent) {
+ options.index = index;
+ model.trigger('remove', model, this, options);
+ }
+
+ removed.push(model);
+ this._removeReference(model, options);
+ }
+ return removed.length ? removed : false;
+ },
+
+ // Method for checking whether an object should be considered a model for
+ // the purposes of adding to the collection.
+ _isModel: function (model) {
+ return model instanceof Model;
+ },
+
+ // Internal method to create a model's ties to a collection.
+ _addReference: function(model, options) {
+ this._byId[model.cid] = model;
+ var id = this.modelId(model.attributes);
+ if (id != null) this._byId[id] = model;
+ model.on('all', this._onModelEvent, this);
+ },
+
+ // Internal method to sever a model's ties to a collection.
+ _removeReference: function(model, options) {
+ delete this._byId[model.cid];
+ var id = this.modelId(model.attributes);
+ if (id != null) delete this._byId[id];
+ if (this === model.collection) delete model.collection;
+ model.off('all', this._onModelEvent, this);
+ },
+
+ // Internal method called every time a model in the set fires an event.
+ // Sets need to update their indexes when models change ids. All other
+ // events simply proxy through. "add" and "remove" events that originate
+ // in other collections are ignored.
+ _onModelEvent: function(event, model, collection, options) {
+ if ((event === 'add' || event === 'remove') && collection !== this) return;
+ if (event === 'destroy') this.remove(model, options);
+ if (event === 'change') {
+ var prevId = this.modelId(model.previousAttributes());
+ var id = this.modelId(model.attributes);
+ if (prevId !== id) {
+ if (prevId != null) delete this._byId[prevId];
+ if (id != null) this._byId[id] = model;
+ }
+ }
+ this.trigger.apply(this, arguments);
}
- };
- // Aliases for backwards compatibility.
- Events.bind = Events.on;
- Events.unbind = Events.off;
+ });
+
+ // Underscore methods that we want to implement on the Collection.
+ // 90% of the core usefulness of Backbone Collections is actually implemented
+ // right here:
+ var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4,
+ foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3,
+ select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 2,
+ contains: 2, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
+ head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
+ without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
+ isEmpty: 1, chain: 1, sample: 3, partition: 3 };
+
+ // Mix in each Underscore method as a proxy to `Collection#models`.
+ addUnderscoreMethods(Collection, collectionMethods, 'models');
- // Allow the `Backbone` object to serve as a global event bus, for folks who
- // want global "pubsub" in a convenient place.
- _.extend(Backbone, Events);
+ // Underscore methods that take a property name as an argument.
+ var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy'];
- // Backbone.Model
- // --------------
+ // Use attributes instead of properties.
+ _.each(attributeMethods, function(method) {
+ if (!_[method]) return;
+ Collection.prototype[method] = function(value, context) {
+ var iterator = _.isFunction(value) ? value : function(model) {
+ return model.get(value);
+ };
+ return _[method](this.models, iterator, context);
+ };
+ });
- // Backbone **Models** are the basic data object in the framework --
- // frequently representing a row in a table in a database on your server.
- // A discrete chunk of data and a bunch of useful, related methods for
- // performing computations and transformations on that data.
+ // Backbone.View
+ // -------------
- // Create a new model with the specified attributes. A client id (`cid`)
- // is automatically generated and assigned for you.
- var Model = Backbone.Model = function(attributes, options) {
- var attrs = attributes || {};
- options || (options = {});
- this.cid = _.uniqueId(this.cidPrefix);
- this.attributes = {};
- if (options.collection) this.collection = options.collection;
- if (options.parse) attrs = this.parse(attrs, options) || {};
- var defaults = _.result(this, 'defaults');
- attrs = _.defaults(_.extend({}, defaults, attrs), defaults);
- this.set(attrs, options);
- this.changed = {};
+ // Backbone Views are almost more convention than they are actual code. A View
+ // is simply a JavaScript object that represents a logical chunk of UI in the
+ // DOM. This might be a single item, an entire list, a sidebar or panel, or
+ // even the surrounding frame which wraps your whole app. Defining a chunk of
+ // UI as a **View** allows you to define your DOM events declaratively, without
+ // having to worry about render order ... and makes it easy for the view to
+ // react to specific changes in the state of your models.
+
+ // Creating a Backbone.View creates its initial element outside of the DOM,
+ // if an existing element is not provided...
+ var View = Backbone.View = function(options) {
+ this.cid = _.uniqueId('view');
+ _.extend(this, _.pick(options, viewOptions));
+ this._ensureElement();
this.initialize.apply(this, arguments);
};
- // Attach all inheritable methods to the Model prototype.
- _.extend(Model.prototype, Events, {
+ // Cached regex to split keys for `delegate`.
+ var delegateEventSplitter = /^(\S+)\s*(.*)$/;
- // A hash of attributes whose current and previous value differ.
- changed: null,
+ // List of view options to be merged as properties.
+ var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
- // The value returned during the last failed validation.
- validationError: null,
+ // Set up all inheritable **Backbone.View** properties and methods.
+ _.extend(View.prototype, Events, {
- // The default name for the JSON `id` attribute is `"id"`. MongoDB and
- // CouchDB users may want to set this to `"_id"`.
- idAttribute: 'id',
+ // The default `tagName` of a View's element is `"div"`.
+ tagName: 'div',
- // The prefix is used to create the client id which is used to identify models locally.
- // You may want to override this if you're experiencing name clashes with model ids.
- cidPrefix: 'c',
+ // jQuery delegate for element lookup, scoped to DOM elements within the
+ // current view. This should be preferred to global lookups where possible.
+ $: function(selector) {
+ return this.$el.find(selector);
+ },
// Initialize is an empty function by default. Override it with your own
// initialization logic.
initialize: function(){},
- // Return a copy of the model's `attributes` object.
- toJSON: function(options) {
- return _.clone(this.attributes);
- },
-
- // Proxy `Backbone.sync` by default -- but override this if you need
- // custom syncing semantics for *this* particular model.
- sync: function() {
- return Backbone.sync.apply(this, arguments);
+ // **render** is the core function that your view should override, in order
+ // to populate its element (`this.el`), with the appropriate HTML. The
+ // convention is for **render** to always return `this`.
+ render: function() {
+ return this;
},
- // Get the value of an attribute.
- get: function(attr) {
- return this.attributes[attr];
+ // Remove this view by taking the element out of the DOM, and removing any
+ // applicable Backbone.Events listeners.
+ remove: function() {
+ this._removeElement();
+ this.stopListening();
+ return this;
},
- // Get the HTML-escaped value of an attribute.
- escape: function(attr) {
- return _.escape(this.get(attr));
+ // Remove this view's element from the document and all event listeners
+ // attached to it. Exposed for subclasses using an alternative DOM
+ // manipulation API.
+ _removeElement: function() {
+ this.$el.remove();
},
- // Returns `true` if the attribute contains a value that is not null
- // or undefined.
- has: function(attr) {
- return this.get(attr) != null;
+ // Change the view's element (`this.el` property) and re-delegate the
+ // view's events on the new element.
+ setElement: function(element) {
+ this.undelegateEvents();
+ this._setElement(element);
+ this.delegateEvents();
+ return this;
},
- // Special-cased proxy to underscore's `_.matches` method.
- matches: function(attrs) {
- return !!_.iteratee(attrs, this)(this.attributes);
+ // Creates the `this.el` and `this.$el` references for this view using the
+ // given `el`. `el` can be a CSS selector or an HTML string, a jQuery
+ // context or an element. Subclasses can override this to utilize an
+ // alternative DOM manipulation API and are only required to set the
+ // `this.el` property.
+ _setElement: function(el) {
+ this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
+ this.el = this.$el[0];
},
- // Set a hash of model attributes on the object, firing `"change"`. This is
- // the core primitive operation of a model, updating the data and notifying
- // anyone who needs to know about the change in state. The heart of the beast.
- set: function(key, val, options) {
- if (key == null) return this;
-
- // Handle both `"key", value` and `{key: value}` -style arguments.
- var attrs;
- if (typeof key === 'object') {
- attrs = key;
- options = val;
- } else {
- (attrs = {})[key] = val;
- }
-
- options || (options = {});
-
- // Run validation.
- if (!this._validate(attrs, options)) return false;
-
- // Extract attributes and options.
- var unset = options.unset;
- var silent = options.silent;
- var changes = [];
- var changing = this._changing;
- this._changing = true;
-
- if (!changing) {
- this._previousAttributes = _.clone(this.attributes);
- this.changed = {};
- }
-
- var current = this.attributes;
- var changed = this.changed;
- var prev = this._previousAttributes;
-
- // For each `set` attribute, update or delete the current value.
- for (var attr in attrs) {
- val = attrs[attr];
- if (!_.isEqual(current[attr], val)) changes.push(attr);
- if (!_.isEqual(prev[attr], val)) {
- changed[attr] = val;
- } else {
- delete changed[attr];
- }
- unset ? delete current[attr] : current[attr] = val;
- }
-
- // Update the `id`.
- if (this.idAttribute in attrs) this.id = this.get(this.idAttribute);
-
- // Trigger all relevant attribute changes.
- if (!silent) {
- if (changes.length) this._pending = options;
- for (var i = 0; i < changes.length; i++) {
- this.trigger('change:' + changes[i], this, current[changes[i]], options);
- }
- }
-
- // You might be wondering why there's a `while` loop here. Changes can
- // be recursively nested within `"change"` events.
- if (changing) return this;
- if (!silent) {
- while (this._pending) {
- options = this._pending;
- this._pending = false;
- this.trigger('change', this, options);
- }
+ // Set callbacks, where `this.events` is a hash of
+ //
+ // *{"event selector": "callback"}*
+ //
+ // {
+ // 'mousedown .title': 'edit',
+ // 'click .button': 'save',
+ // 'click .open': function(e) { ... }
+ // }
+ //
+ // pairs. Callbacks will be bound to the view, with `this` set properly.
+ // Uses event delegation for efficiency.
+ // Omitting the selector binds the event to `this.el`.
+ delegateEvents: function(events) {
+ events || (events = _.result(this, 'events'));
+ if (!events) return this;
+ this.undelegateEvents();
+ for (var key in events) {
+ var method = events[key];
+ if (!_.isFunction(method)) method = this[method];
+ if (!method) continue;
+ var match = key.match(delegateEventSplitter);
+ this.delegate(match[1], match[2], _.bind(method, this));
}
- this._pending = false;
- this._changing = false;
return this;
},
- // Remove an attribute from the model, firing `"change"`. `unset` is a noop
- // if the attribute doesn't exist.
- unset: function(attr, options) {
- return this.set(attr, void 0, _.extend({}, options, {unset: true}));
- },
-
- // Clear all attributes on the model, firing `"change"`.
- clear: function(options) {
- var attrs = {};
- for (var key in this.attributes) attrs[key] = void 0;
- return this.set(attrs, _.extend({}, options, {unset: true}));
- },
-
- // Determine if the model has changed since the last `"change"` event.
- // If you specify an attribute name, determine if that attribute has changed.
- hasChanged: function(attr) {
- if (attr == null) return !_.isEmpty(this.changed);
- return _.has(this.changed, attr);
- },
-
- // Return an object containing all the attributes that have changed, or
- // false if there are no changed attributes. Useful for determining what
- // parts of a view need to be updated and/or what attributes need to be
- // persisted to the server. Unset attributes will be set to undefined.
- // You can also pass an attributes object to diff against the model,
- // determining if there *would be* a change.
- changedAttributes: function(diff) {
- if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
- var old = this._changing ? this._previousAttributes : this.attributes;
- var changed = {};
- for (var attr in diff) {
- var val = diff[attr];
- if (_.isEqual(old[attr], val)) continue;
- changed[attr] = val;
- }
- return _.size(changed) ? changed : false;
+ // Add a single event listener to the view's element (or a child element
+ // using `selector`). This only works for delegate-able events: not `focus`,
+ // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
+ delegate: function(eventName, selector, listener) {
+ this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener);
+ return this;
},
- // Get the previous value of an attribute, recorded at the time the last
- // `"change"` event was fired.
- previous: function(attr) {
- if (attr == null || !this._previousAttributes) return null;
- return this._previousAttributes[attr];
+ // Clears all callbacks previously bound to the view by `delegateEvents`.
+ // You usually don't need to use this, but may wish to if you have multiple
+ // Backbone views attached to the same DOM element.
+ undelegateEvents: function() {
+ if (this.$el) this.$el.off('.delegateEvents' + this.cid);
+ return this;
},
- // Get all of the attributes of the model at the time of the previous
- // `"change"` event.
- previousAttributes: function() {
- return _.clone(this._previousAttributes);
+ // A finer-grained `undelegateEvents` for removing a single delegated event.
+ // `selector` and `listener` are both optional.
+ undelegate: function(eventName, selector, listener) {
+ this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener);
+ return this;
},
- // Fetch the model from the server, merging the response with the model's
- // local attributes. Any changed attributes will trigger a "change" event.
- fetch: function(options) {
- options = _.extend({parse: true}, options);
- var model = this;
- var success = options.success;
- options.success = function(resp) {
- var serverAttrs = options.parse ? model.parse(resp, options) : resp;
- if (!model.set(serverAttrs, options)) return false;
- if (success) success.call(options.context, model, resp, options);
- model.trigger('sync', model, resp, options);
- };
- wrapError(this, options);
- return this.sync('read', this, options);
+ // Produces a DOM element to be assigned to your view. Exposed for
+ // subclasses using an alternative DOM manipulation API.
+ _createElement: function(tagName) {
+ return document.createElement(tagName);
},
- // Set a hash of model attributes, and sync the model to the server.
- // If the server returns an attributes hash that differs, the model's
- // state will be `set` again.
- save: function(key, val, options) {
- // Handle both `"key", value` and `{key: value}` -style arguments.
- var attrs;
- if (key == null || typeof key === 'object') {
- attrs = key;
- options = val;
+ // Ensure that the View has a DOM element to render into.
+ // If `this.el` is a string, pass it through `$()`, take the first
+ // matching element, and re-assign it to `el`. Otherwise, create
+ // an element from the `id`, `className` and `tagName` properties.
+ _ensureElement: function() {
+ if (!this.el) {
+ var attrs = _.extend({}, _.result(this, 'attributes'));
+ if (this.id) attrs.id = _.result(this, 'id');
+ if (this.className) attrs['class'] = _.result(this, 'className');
+ this.setElement(this._createElement(_.result(this, 'tagName')));
+ this._setAttributes(attrs);
} else {
- (attrs = {})[key] = val;
- }
-
- options = _.extend({validate: true, parse: true}, options);
- var wait = options.wait;
-
- // If we're not waiting and attributes exist, save acts as
- // `set(attr).save(null, opts)` with validation. Otherwise, check if
- // the model will be valid when the attributes, if any, are set.
- if (attrs && !wait) {
- if (!this.set(attrs, options)) return false;
- } else if (!this._validate(attrs, options)) {
- return false;
+ this.setElement(_.result(this, 'el'));
}
-
- // After a successful server-side save, the client is (optionally)
- // updated with the server-side state.
- var model = this;
- var success = options.success;
- var attributes = this.attributes;
- options.success = function(resp) {
- // Ensure attributes are restored during synchronous saves.
- model.attributes = attributes;
- var serverAttrs = options.parse ? model.parse(resp, options) : resp;
- if (wait) serverAttrs = _.extend({}, attrs, serverAttrs);
- if (serverAttrs && !model.set(serverAttrs, options)) return false;
- if (success) success.call(options.context, model, resp, options);
- model.trigger('sync', model, resp, options);
- };
- wrapError(this, options);
-
- // Set temporary attributes if `{wait: true}` to properly find new ids.
- if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
-
- var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
- if (method === 'patch' && !options.attrs) options.attrs = attrs;
- var xhr = this.sync(method, this, options);
-
- // Restore attributes.
- this.attributes = attributes;
-
- return xhr;
},
- // Destroy this model on the server if it was already persisted.
- // Optimistically removes the model from its collection, if it has one.
- // If `wait: true` is passed, waits for the server to respond before removal.
- destroy: function(options) {
- options = options ? _.clone(options) : {};
- var model = this;
- var success = options.success;
- var wait = options.wait;
-
- var destroy = function() {
- model.stopListening();
- model.trigger('destroy', model, model.collection, options);
- };
+ // Set attributes from a hash on this view's element. Exposed for
+ // subclasses using an alternative DOM manipulation API.
+ _setAttributes: function(attributes) {
+ this.$el.attr(attributes);
+ }
- options.success = function(resp) {
- if (wait) destroy();
- if (success) success.call(options.context, model, resp, options);
- if (!model.isNew()) model.trigger('sync', model, resp, options);
- };
+ });
- var xhr = false;
- if (this.isNew()) {
- _.defer(options.success);
- } else {
- wrapError(this, options);
- xhr = this.sync('delete', this, options);
- }
- if (!wait) destroy();
- return xhr;
- },
+ // Backbone.sync
+ // -------------
- // Default URL for the model's representation on the server -- if you're
- // using Backbone's restful methods, override this to change the endpoint
- // that will be called.
- url: function() {
- var base =
- _.result(this, 'urlRoot') ||
- _.result(this.collection, 'url') ||
- urlError();
- if (this.isNew()) return base;
- var id = this.get(this.idAttribute);
- return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
- },
+ // Override this function to change the manner in which Backbone persists
+ // models to the server. You will be passed the type of request, and the
+ // model in question. By default, makes a RESTful Ajax request
+ // to the model's `url()`. Some possible customizations could be:
+ //
+ // * Use `setTimeout` to batch rapid-fire updates into a single request.
+ // * Send up the models as XML instead of JSON.
+ // * Persist models via WebSockets instead of Ajax.
+ //
+ // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
+ // as `POST`, with a `_method` parameter containing the true HTTP method,
+ // as well as all requests with the body as `application/x-www-form-urlencoded`
+ // instead of `application/json` with the model in a param named `model`.
+ // Useful when interfacing with server-side languages like **PHP** that make
+ // it difficult to read the body of `PUT` requests.
+ Backbone.sync = function(method, model, options) {
+ var type = methodMap[method];
- // **parse** converts a response into the hash of attributes to be `set` on
- // the model. The default implementation is just to pass the response along.
- parse: function(resp, options) {
- return resp;
- },
+ // Default options, unless specified.
+ _.defaults(options || (options = {}), {
+ emulateHTTP: Backbone.emulateHTTP,
+ emulateJSON: Backbone.emulateJSON
+ });
- // Create a new model with identical attributes to this one.
- clone: function() {
- return new this.constructor(this.attributes);
- },
+ // Default JSON-request options.
+ var params = {type: type, dataType: 'json'};
- // A model is new if it has never been saved to the server, and lacks an id.
- isNew: function() {
- return !this.has(this.idAttribute);
- },
+ // Ensure that we have a URL.
+ if (!options.url) {
+ params.url = _.result(model, 'url') || urlError();
+ }
- // Check if the model is currently in a valid state.
- isValid: function(options) {
- return this._validate({}, _.extend({}, options, {validate: true}));
- },
+ // Ensure that we have the appropriate request data.
+ if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
+ params.contentType = 'application/json';
+ params.data = JSON.stringify(options.attrs || model.toJSON(options));
+ }
- // Run validation against the next complete set of model attributes,
- // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
- _validate: function(attrs, options) {
- if (!options.validate || !this.validate) return true;
- attrs = _.extend({}, this.attributes, attrs);
- var error = this.validationError = this.validate(attrs, options) || null;
- if (!error) return true;
- this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
- return false;
+ // For older servers, emulate JSON by encoding the request into an HTML-form.
+ if (options.emulateJSON) {
+ params.contentType = 'application/x-www-form-urlencoded';
+ params.data = params.data ? {model: params.data} : {};
}
- });
+ // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
+ // And an `X-HTTP-Method-Override` header.
+ if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
+ params.type = 'POST';
+ if (options.emulateJSON) params.data._method = type;
+ var beforeSend = options.beforeSend;
+ options.beforeSend = function(xhr) {
+ xhr.setRequestHeader('X-HTTP-Method-Override', type);
+ if (beforeSend) return beforeSend.apply(this, arguments);
+ };
+ }
- // Underscore methods that we want to implement on the Model, mapped to the
- // number of arguments they take.
- var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
- omit: 0, chain: 1, isEmpty: 1};
+ // Don't process data on a non-GET request.
+ if (params.type !== 'GET' && !options.emulateJSON) {
+ params.processData = false;
+ }
- // Mix in each Underscore method as a proxy to `Model#attributes`.
- addUnderscoreMethods(Model, modelMethods, 'attributes');
+ // Pass along `textStatus` and `errorThrown` from jQuery.
+ var error = options.error;
+ options.error = function(xhr, textStatus, errorThrown) {
+ options.textStatus = textStatus;
+ options.errorThrown = errorThrown;
+ if (error) error.call(options.context, xhr, textStatus, errorThrown);
+ };
- // Backbone.Collection
- // -------------------
+ // Make the request, allowing the user to override any Ajax options.
+ var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
+ model.trigger('request', model, xhr, options);
+ return xhr;
+ };
- // If models tend to represent a single row of data, a Backbone Collection is
- // more analogous to a table full of data ... or a small slice or page of that
- // table, or a collection of rows that belong together for a particular reason
- // -- all of the messages in this particular folder, all of the documents
- // belonging to this particular author, and so on. Collections maintain
- // indexes of their models, both in order, and for lookup by `id`.
+ // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
+ var methodMap = {
+ 'create': 'POST',
+ 'update': 'PUT',
+ 'patch': 'PATCH',
+ 'delete': 'DELETE',
+ 'read': 'GET'
+ };
- // Create a new **Collection**, perhaps to contain a specific type of `model`.
- // If a `comparator` is specified, the Collection will maintain
- // its models in sort order, as they're added and removed.
- var Collection = Backbone.Collection = function(models, options) {
- options || (options = {});
- if (options.model) this.model = options.model;
- if (options.comparator !== void 0) this.comparator = options.comparator;
- this._reset();
- this.initialize.apply(this, arguments);
- if (models) this.reset(models, _.extend({silent: true}, options));
+ // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
+ // Override this if you'd like to use a different library.
+ Backbone.ajax = function() {
+ return Backbone.$.ajax.apply(Backbone.$, arguments);
};
- // Default options for `Collection#set`.
- var setOptions = {add: true, remove: true, merge: true};
- var addOptions = {add: true, remove: false};
+ // Backbone.Router
+ // ---------------
- // Splices `insert` into `array` at index `at`.
- var splice = function(array, insert, at) {
- at = Math.min(Math.max(at, 0), array.length);
- var tail = Array(array.length - at);
- var length = insert.length;
- var i;
- for (i = 0; i < tail.length; i++) tail[i] = array[i + at];
- for (i = 0; i < length; i++) array[i + at] = insert[i];
- for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
+ // Routers map faux-URLs to actions, and fire events when routes are
+ // matched. Creating a new one sets its `routes` hash, if not set statically.
+ var Router = Backbone.Router = function(options) {
+ options || (options = {});
+ if (options.routes) this.routes = options.routes;
+ this._bindRoutes();
+ this.initialize.apply(this, arguments);
};
- // Define the Collection's inheritable methods.
- _.extend(Collection.prototype, Events, {
+ // Cached regular expressions for matching named param parts and splatted
+ // parts of route strings.
+ var optionalParam = /\((.*?)\)/g;
+ var namedParam = /(\(\?)?:\w+/g;
+ var splatParam = /\*\w+/g;
+ var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
- // The default model for a collection is just a **Backbone.Model**.
- // This should be overridden in most cases.
- model: Model,
+ // Set up all inheritable **Backbone.Router** properties and methods.
+ _.extend(Router.prototype, Events, {
// Initialize is an empty function by default. Override it with your own
// initialization logic.
initialize: function(){},
- // The JSON representation of a Collection is an array of the
- // models' attributes.
- toJSON: function(options) {
- return this.map(function(model) { return model.toJSON(options); });
+ // Manually bind a single named route to a callback. For example:
+ //
+ // this.route('search/:query/p:num', 'search', function(query, num) {
+ // ...
+ // });
+ //
+ route: function(route, name, callback) {
+ if (!_.isRegExp(route)) route = this._routeToRegExp(route);
+ if (_.isFunction(name)) {
+ callback = name;
+ name = '';
+ }
+ if (!callback) callback = this[name];
+ var router = this;
+ Backbone.history.route(route, function(fragment) {
+ var args = router._extractParameters(route, fragment);
+ if (router.execute(callback, args, name) !== false) {
+ router.trigger.apply(router, ['route:' + name].concat(args));
+ router.trigger('route', name, args);
+ Backbone.history.trigger('route', router, name, args);
+ }
+ });
+ return this;
},
- // Proxy `Backbone.sync` by default.
- sync: function() {
- return Backbone.sync.apply(this, arguments);
+ // Execute a route handler with the provided parameters. This is an
+ // excellent place to do pre-route setup or post-route cleanup.
+ execute: function(callback, args, name) {
+ if (callback) callback.apply(this, args);
},
- // Add a model, or list of models to the set. `models` may be Backbone
- // Models or raw JavaScript objects to be converted to Models, or any
- // combination of the two.
- add: function(models, options) {
- return this.set(models, _.extend({merge: false}, options, addOptions));
+ // Simple proxy to `Backbone.history` to save a fragment into the history.
+ navigate: function(fragment, options) {
+ Backbone.history.navigate(fragment, options);
+ return this;
},
- // Remove a model, or a list of models from the set.
- remove: function(models, options) {
- options = _.extend({}, options);
- var singular = !_.isArray(models);
- models = singular ? [models] : models.slice();
- var removed = this._removeModels(models, options);
- if (!options.silent && removed.length) {
- options.changes = {added: [], merged: [], removed: removed};
- this.trigger('update', this, options);
+ // Bind all defined routes to `Backbone.history`. We have to reverse the
+ // order of the routes here to support behavior where the most general
+ // routes can be defined at the bottom of the route map.
+ _bindRoutes: function() {
+ if (!this.routes) return;
+ this.routes = _.result(this, 'routes');
+ var route, routes = _.keys(this.routes);
+ while ((route = routes.pop()) != null) {
+ this.route(route, this.routes[route]);
}
- return singular ? removed[0] : removed;
},
- // Update a collection by `set`-ing a new list of models, adding new ones,
- // removing models that are no longer present, and merging models that
- // already exist in the collection, as necessary. Similar to **Model#set**,
- // the core operation for updating the data contained by the collection.
- set: function(models, options) {
- if (models == null) return;
-
- options = _.extend({}, setOptions, options);
- if (options.parse && !this._isModel(models)) {
- models = this.parse(models, options) || [];
- }
-
- var singular = !_.isArray(models);
- models = singular ? [models] : models.slice();
-
- var at = options.at;
- if (at != null) at = +at;
- if (at > this.length) at = this.length;
- if (at < 0) at += this.length + 1;
+ // Convert a route string into a regular expression, suitable for matching
+ // against the current location hash.
+ _routeToRegExp: function(route) {
+ route = route.replace(escapeRegExp, '\\$&')
+ .replace(optionalParam, '(?:$1)?')
+ .replace(namedParam, function(match, optional) {
+ return optional ? match : '([^/?]+)';
+ })
+ .replace(splatParam, '([^?]*?)');
+ return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
+ },
- var set = [];
- var toAdd = [];
- var toMerge = [];
- var toRemove = [];
- var modelMap = {};
+ // Given a route, and a URL fragment that it matches, return the array of
+ // extracted decoded parameters. Empty or unmatched parameters will be
+ // treated as `null` to normalize cross-browser behavior.
+ _extractParameters: function(route, fragment) {
+ var params = route.exec(fragment).slice(1);
+ return _.map(params, function(param, i) {
+ // Don't decode the search params.
+ if (i === params.length - 1) return param || null;
+ return param ? decodeURIComponent(param) : null;
+ });
+ }
- var add = options.add;
- var merge = options.merge;
- var remove = options.remove;
+ });
- var sort = false;
- var sortable = this.comparator && at == null && options.sort !== false;
- var sortAttr = _.isString(this.comparator) ? this.comparator : null;
+ // Backbone.History
+ // ----------------
- // Turn bare objects into model references, and prevent invalid models
- // from being added.
- var model, i;
- for (i = 0; i < models.length; i++) {
- model = models[i];
+ // Handles cross-browser history management, based on either
+ // [pushState](http://diveintohtml5.info/history.html) and real URLs, or
+ // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
+ // and URL fragments. If the browser supports neither (old IE, natch),
+ // falls back to polling.
+ var History = Backbone.History = function() {
+ this.handlers = [];
+ _.bindAll(this, 'checkUrl');
- // If a duplicate is found, prevent it from being added and
- // optionally merge it into the existing model.
- var existing = this.get(model);
- if (existing) {
- if (merge && model !== existing) {
- var attrs = this._isModel(model) ? model.attributes : model;
- if (options.parse) attrs = existing.parse(attrs, options);
- existing.set(attrs, options);
- toMerge.push(existing);
- if (sortable && !sort) sort = existing.hasChanged(sortAttr);
- }
- if (!modelMap[existing.cid]) {
- modelMap[existing.cid] = true;
- set.push(existing);
- }
- models[i] = existing;
+ // Ensure that `History` can be used outside of the browser.
+ if (typeof window !== 'undefined') {
+ this.location = window.location;
+ this.history = window.history;
+ }
+ };
- // If this is a new, valid model, push it to the `toAdd` list.
- } else if (add) {
- model = models[i] = this._prepareModel(model, options);
- if (model) {
- toAdd.push(model);
- this._addReference(model, options);
- modelMap[model.cid] = true;
- set.push(model);
- }
- }
- }
+ // Cached regex for stripping a leading hash/slash and trailing space.
+ var routeStripper = /^[#\/]|\s+$/g;
- // Remove stale models.
- if (remove) {
- for (i = 0; i < this.length; i++) {
- model = this.models[i];
- if (!modelMap[model.cid]) toRemove.push(model);
- }
- if (toRemove.length) this._removeModels(toRemove, options);
- }
+ // Cached regex for stripping leading and trailing slashes.
+ var rootStripper = /^\/+|\/+$/g;
- // See if sorting is needed, update `length` and splice in new models.
- var orderChanged = false;
- var replace = !sortable && add && remove;
- if (set.length && replace) {
- orderChanged = this.length !== set.length || _.some(this.models, function(m, index) {
- return m !== set[index];
- });
- this.models.length = 0;
- splice(this.models, set, 0);
- this.length = this.models.length;
- } else if (toAdd.length) {
- if (sortable) sort = true;
- splice(this.models, toAdd, at == null ? this.length : at);
- this.length = this.models.length;
- }
+ // Cached regex for stripping urls of hash.
+ var pathStripper = /#.*$/;
- // Silently sort the collection if appropriate.
- if (sort) this.sort({silent: true});
+ // Has the history handling already been started?
+ History.started = false;
- // Unless silenced, it's time to fire all appropriate add/sort/update events.
- if (!options.silent) {
- for (i = 0; i < toAdd.length; i++) {
- if (at != null) options.index = at + i;
- model = toAdd[i];
- model.trigger('add', model, this, options);
- }
- if (sort || orderChanged) this.trigger('sort', this, options);
- if (toAdd.length || toRemove.length || toMerge.length) {
- options.changes = {
- added: toAdd,
- removed: toRemove,
- merged: toMerge
- };
- this.trigger('update', this, options);
- }
- }
+ // Set up all inheritable **Backbone.History** properties and methods.
+ _.extend(History.prototype, Events, {
- // Return the added (or merged) model (or models).
- return singular ? models[0] : models;
- },
+ // The default interval to poll for hash changes, if necessary, is
+ // twenty times a second.
+ interval: 50,
- // When you have more items than you want to add or remove individually,
- // you can reset the entire set with a new list of models, without firing
- // any granular `add` or `remove` events. Fires `reset` when finished.
- // Useful for bulk operations and optimizations.
- reset: function(models, options) {
- options = options ? _.clone(options) : {};
- for (var i = 0; i < this.models.length; i++) {
- this._removeReference(this.models[i], options);
- }
- options.previousModels = this.models;
- this._reset();
- models = this.add(models, _.extend({silent: true}, options));
- if (!options.silent) this.trigger('reset', this, options);
- return models;
+ // Are we at the app root?
+ atRoot: function() {
+ var path = this.location.pathname.replace(/[^\/]$/, '$&/');
+ return path === this.root && !this.getSearch();
},
- // Add a model to the end of the collection.
- push: function(model, options) {
- return this.add(model, _.extend({at: this.length}, options));
+ // Does the pathname match the root?
+ matchRoot: function() {
+ var path = this.decodeFragment(this.location.pathname);
+ var root = path.slice(0, this.root.length - 1) + '/';
+ return root === this.root;
},
- // Remove a model from the end of the collection.
- pop: function(options) {
- var model = this.at(this.length - 1);
- return this.remove(model, options);
+ // Unicode characters in `location.pathname` are percent encoded so they're
+ // decoded for comparison. `%25` should not be decoded since it may be part
+ // of an encoded parameter.
+ decodeFragment: function(fragment) {
+ return decodeURI(fragment.replace(/%25/g, '%2525'));
},
- // Add a model to the beginning of the collection.
- unshift: function(model, options) {
- return this.add(model, _.extend({at: 0}, options));
+ // In IE6, the hash fragment and search params are incorrect if the
+ // fragment contains `?`.
+ getSearch: function() {
+ var match = this.location.href.replace(/#.*/, '').match(/\?.+/);
+ return match ? match[0] : '';
},
- // Remove a model from the beginning of the collection.
- shift: function(options) {
- var model = this.at(0);
- return this.remove(model, options);
+ // Gets the true hash value. Cannot use location.hash directly due to bug
+ // in Firefox where location.hash will always be decoded.
+ getHash: function(window) {
+ var match = (window || this).location.href.match(/#(.*)$/);
+ return match ? match[1] : '';
},
- // Slice out a sub-array of models from the collection.
- slice: function() {
- return slice.apply(this.models, arguments);
+ // Get the pathname and search params, without the root.
+ getPath: function() {
+ var path = this.decodeFragment(
+ this.location.pathname + this.getSearch()
+ ).slice(this.root.length - 1);
+ return path.charAt(0) === '/' ? path.slice(1) : path;
},
- // Get a model from the set by id, cid, model object with id or cid
- // properties, or an attributes object that is transformed through modelId.
- get: function(obj) {
- if (obj == null) return void 0;
- return this._byId[obj] ||
- this._byId[this.modelId(obj.attributes || obj)] ||
- obj.cid && this._byId[obj.cid];
+ // Get the cross-browser normalized URL fragment from the path or hash.
+ getFragment: function(fragment) {
+ if (fragment == null) {
+ if (this._usePushState || !this._wantsHashChange) {
+ fragment = this.getPath();
+ } else {
+ fragment = this.getHash();
+ }
+ }
+ return fragment.replace(routeStripper, '');
},
- // Returns `true` if the model is in the collection.
- has: function(obj) {
- return this.get(obj) != null;
- },
+ // Start the hash change handling, returning `true` if the current URL matches
+ // an existing route, and `false` otherwise.
+ start: function(options) {
+ if (History.started) throw new Error('Backbone.history has already been started');
+ History.started = true;
- // Get the model at the given index.
- at: function(index) {
- if (index < 0) index += this.length;
- return this.models[index];
- },
+ // Figure out the initial configuration. Do we need an iframe?
+ // Is pushState desired ... is it available?
+ this.options = _.extend({root: '/'}, this.options, options);
+ this.root = this.options.root;
+ this._wantsHashChange = this.options.hashChange !== false;
+ this._hasHashChange = 'onhashchange' in window;
+ this._useHashChange = this._wantsHashChange && this._hasHashChange;
+ this._wantsPushState = !!this.options.pushState;
+ this._hasPushState = !!(this.history && this.history.pushState);
+ this._usePushState = this._wantsPushState && this._hasPushState;
+ this.fragment = this.getFragment();
- // Return models with matching attributes. Useful for simple cases of
- // `filter`.
- where: function(attrs, first) {
- return this[first ? 'find' : 'filter'](attrs);
- },
+ // Normalize root to always include a leading and trailing slash.
+ this.root = ('/' + this.root + '/').replace(rootStripper, '/');
- // Return the first model with matching attributes. Useful for simple cases
- // of `find`.
- findWhere: function(attrs) {
- return this.where(attrs, true);
- },
+ // Transition from hashChange to pushState or vice versa if both are
+ // requested.
+ if (this._wantsHashChange && this._wantsPushState) {
- // Force the collection to re-sort itself. You don't need to call this under
- // normal circumstances, as the set will maintain sort order as each item
- // is added.
- sort: function(options) {
- var comparator = this.comparator;
- if (!comparator) throw new Error('Cannot sort a set without a comparator');
- options || (options = {});
+ // If we've started off with a route from a `pushState`-enabled
+ // browser, but we're currently in a browser that doesn't support it...
+ if (!this._hasPushState && !this.atRoot()) {
+ var root = this.root.slice(0, -1) || '/';
+ this.location.replace(root + '#' + this.getPath());
+ // Return immediately as browser will do redirect to new url
+ return true;
- var length = comparator.length;
- if (_.isFunction(comparator)) comparator = _.bind(comparator, this);
+ // Or if we've started out with a hash-based route, but we're currently
+ // in a browser where it could be `pushState`-based instead...
+ } else if (this._hasPushState && this.atRoot()) {
+ this.navigate(this.getHash(), {replace: true});
+ }
- // Run sort based on type of `comparator`.
- if (length === 1 || _.isString(comparator)) {
- this.models = this.sortBy(comparator);
- } else {
- this.models.sort(comparator);
}
- if (!options.silent) this.trigger('sort', this, options);
- return this;
- },
- // Pluck an attribute from each model in the collection.
- pluck: function(attr) {
- return this.map(attr + '');
- },
+ // Proxy an iframe to handle location events if the browser doesn't
+ // support the `hashchange` event, HTML5 history, or the user wants
+ // `hashChange` but not `pushState`.
+ if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) {
+ this.iframe = document.createElement('iframe');
+ this.iframe.src = 'javascript:0';
+ this.iframe.style.display = 'none';
+ this.iframe.tabIndex = -1;
+ var body = document.body;
+ // Using `appendChild` will throw on IE < 9 if the document is not ready.
+ var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow;
+ iWindow.document.open();
+ iWindow.document.close();
+ iWindow.location.hash = '#' + this.fragment;
+ }
- // Fetch the default set of models for this collection, resetting the
- // collection when they arrive. If `reset: true` is passed, the response
- // data will be passed through the `reset` method instead of `set`.
- fetch: function(options) {
- options = _.extend({parse: true}, options);
- var success = options.success;
- var collection = this;
- options.success = function(resp) {
- var method = options.reset ? 'reset' : 'set';
- collection[method](resp, options);
- if (success) success.call(options.context, collection, resp, options);
- collection.trigger('sync', collection, resp, options);
+ // Add a cross-platform `addEventListener` shim for older browsers.
+ var addEventListener = window.addEventListener || function (eventName, listener) {
+ return attachEvent('on' + eventName, listener);
};
- wrapError(this, options);
- return this.sync('read', this, options);
+
+ // Depending on whether we're using pushState or hashes, and whether
+ // 'onhashchange' is supported, determine how we check the URL state.
+ if (this._usePushState) {
+ addEventListener('popstate', this.checkUrl, false);
+ } else if (this._useHashChange && !this.iframe) {
+ addEventListener('hashchange', this.checkUrl, false);
+ } else if (this._wantsHashChange) {
+ this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
+ }
+
+ if (!this.options.silent) return this.loadUrl();
},
- // Create a new instance of a model in this collection. Add the model to the
- // collection immediately, unless `wait: true` is passed, in which case we
- // wait for the server to agree.
- create: function(model, options) {
- options = options ? _.clone(options) : {};
- var wait = options.wait;
- model = this._prepareModel(model, options);
- if (!model) return false;
- if (!wait) this.add(model, options);
- var collection = this;
- var success = options.success;
- options.success = function(m, resp, callbackOpts) {
- if (wait) collection.add(m, callbackOpts);
- if (success) success.call(callbackOpts.context, m, resp, callbackOpts);
+ // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
+ // but possibly useful for unit testing Routers.
+ stop: function() {
+ // Add a cross-platform `removeEventListener` shim for older browsers.
+ var removeEventListener = window.removeEventListener || function (eventName, listener) {
+ return detachEvent('on' + eventName, listener);
};
- model.save(null, options);
- return model;
- },
- // **parse** converts a response into a list of models to be added to the
- // collection. The default implementation is just to pass it through.
- parse: function(resp, options) {
- return resp;
- },
+ // Remove window listeners.
+ if (this._usePushState) {
+ removeEventListener('popstate', this.checkUrl, false);
+ } else if (this._useHashChange && !this.iframe) {
+ removeEventListener('hashchange', this.checkUrl, false);
+ }
+
+ // Clean up the iframe if necessary.
+ if (this.iframe) {
+ document.body.removeChild(this.iframe);
+ this.iframe = null;
+ }
- // Create a new collection with an identical list of models as this one.
- clone: function() {
- return new this.constructor(this.models, {
- model: this.model,
- comparator: this.comparator
- });
+ // Some environments will throw when clearing an undefined interval.
+ if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
+ History.started = false;
},
- // Define how to uniquely identify models in the collection.
- modelId: function(attrs) {
- return attrs[this.model.prototype.idAttribute || 'id'];
+ // Add a route to be tested when the fragment changes. Routes added later
+ // may override previous routes.
+ route: function(route, callback) {
+ this.handlers.unshift({route: route, callback: callback});
},
- // Private method to reset all internal state. Called when the collection
- // is first initialized or reset.
- _reset: function() {
- this.length = 0;
- this.models = [];
- this._byId = {};
- },
+ // Checks the current URL to see if it has changed, and if it has,
+ // calls `loadUrl`, normalizing across the hidden iframe.
+ checkUrl: function(e) {
+ var current = this.getFragment();
- // Prepare a hash of attributes (or other model) to be added to this
- // collection.
- _prepareModel: function(attrs, options) {
- if (this._isModel(attrs)) {
- if (!attrs.collection) attrs.collection = this;
- return attrs;
+ // If the user pressed the back button, the iframe's hash will have
+ // changed and we should use that for comparison.
+ if (current === this.fragment && this.iframe) {
+ current = this.getHash(this.iframe.contentWindow);
}
- options = options ? _.clone(options) : {};
- options.collection = this;
- var model = new this.model(attrs, options);
- if (!model.validationError) return model;
- this.trigger('invalid', this, model.validationError, options);
- return false;
- },
- // Internal method called by both remove and set.
- _removeModels: function(models, options) {
- var removed = [];
- for (var i = 0; i < models.length; i++) {
- var model = this.get(models[i]);
- if (!model) continue;
+ if (current === this.fragment) return false;
+ if (this.iframe) this.navigate(current);
+ this.loadUrl();
+ },
- var index = this.indexOf(model);
- this.models.splice(index, 1);
- this.length--;
+ // Attempt to load the current URL fragment. If a route succeeds with a
+ // match, returns `true`. If no defined routes matches the fragment,
+ // returns `false`.
+ loadUrl: function(fragment) {
+ // If the root doesn't match, no routes can match either.
+ if (!this.matchRoot()) return false;
+ fragment = this.fragment = this.getFragment(fragment);
+ return _.any(this.handlers, function(handler) {
+ if (handler.route.test(fragment)) {
+ handler.callback(fragment);
+ return true;
+ }
+ });
+ },
- // Remove references before triggering 'remove' event to prevent an
- // infinite loop. #3693
- delete this._byId[model.cid];
- var id = this.modelId(model.attributes);
- if (id != null) delete this._byId[id];
+ // Save a fragment into the hash history, or replace the URL state if the
+ // 'replace' option is passed. You are responsible for properly URL-encoding
+ // the fragment in advance.
+ //
+ // The options object can contain `trigger: true` if you wish to have the
+ // route callback be fired (not usually desirable), or `replace: true`, if
+ // you wish to modify the current URL without adding an entry to the history.
+ navigate: function(fragment, options) {
+ if (!History.started) return false;
+ if (!options || options === true) options = {trigger: !!options};
- if (!options.silent) {
- options.index = index;
- model.trigger('remove', model, this, options);
- }
+ // Normalize the fragment.
+ fragment = this.getFragment(fragment || '');
- removed.push(model);
- this._removeReference(model, options);
+ // Don't include a trailing slash on the root.
+ var root = this.root;
+ if (fragment === '' || fragment.charAt(0) === '?') {
+ root = root.slice(0, -1) || '/';
}
- return removed;
- },
+ var url = root + fragment;
- // Method for checking whether an object should be considered a model for
- // the purposes of adding to the collection.
- _isModel: function(model) {
- return model instanceof Model;
- },
+ // Strip the hash and decode for matching.
+ fragment = this.decodeFragment(fragment.replace(pathStripper, ''));
- // Internal method to create a model's ties to a collection.
- _addReference: function(model, options) {
- this._byId[model.cid] = model;
- var id = this.modelId(model.attributes);
- if (id != null) this._byId[id] = model;
- model.on('all', this._onModelEvent, this);
- },
+ if (this.fragment === fragment) return;
+ this.fragment = fragment;
- // Internal method to sever a model's ties to a collection.
- _removeReference: function(model, options) {
- delete this._byId[model.cid];
- var id = this.modelId(model.attributes);
- if (id != null) delete this._byId[id];
- if (this === model.collection) delete model.collection;
- model.off('all', this._onModelEvent, this);
- },
+ // If pushState is available, we use it to set the fragment as a real URL.
+ if (this._usePushState) {
+ this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
- // Internal method called every time a model in the set fires an event.
- // Sets need to update their indexes when models change ids. All other
- // events simply proxy through. "add" and "remove" events that originate
- // in other collections are ignored.
- _onModelEvent: function(event, model, collection, options) {
- if (model) {
- if ((event === 'add' || event === 'remove') && collection !== this) return;
- if (event === 'destroy') this.remove(model, options);
- if (event === 'change') {
- var prevId = this.modelId(model.previousAttributes());
- var id = this.modelId(model.attributes);
- if (prevId !== id) {
- if (prevId != null) delete this._byId[prevId];
- if (id != null) this._byId[id] = model;
+ // If hash changes haven't been explicitly disabled, update the hash
+ // fragment to store history.
+ } else if (this._wantsHashChange) {
+ this._updateHash(this.location, fragment, options.replace);
+ if (this.iframe && (fragment !== this.getHash(this.iframe.contentWindow))) {
+ var iWindow = this.iframe.contentWindow;
+
+ // Opening and closing the iframe tricks IE7 and earlier to push a
+ // history entry on hash-tag change. When replace is true, we don't
+ // want this.
+ if (!options.replace) {
+ iWindow.document.open();
+ iWindow.document.close();
}
+
+ this._updateHash(iWindow.location, fragment, options.replace);
}
+
+ // If you've told us that you explicitly don't want fallback hashchange-
+ // based history, then `navigate` becomes a page refresh.
+ } else {
+ return this.location.assign(url);
+ }
+ if (options.trigger) return this.loadUrl(fragment);
+ },
+
+ // Update the hash location, either replacing the current entry, or adding
+ // a new one to the browser history.
+ _updateHash: function(location, fragment, replace) {
+ if (replace) {
+ var href = location.href.replace(/(javascript:|#).*$/, '');
+ location.replace(href + '#' + fragment);
+ } else {
+ // Some browsers require that `hash` contains a leading #.
+ location.hash = '#' + fragment;
}
- this.trigger.apply(this, arguments);
}
});
- // Underscore methods that we want to implement on the Collection.
- // 90% of the core usefulness of Backbone Collections is actually implemented
- // right here:
- var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0,
- foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3,
- select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
- contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
- head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
- without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
- isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
- sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
+ // Create the default Backbone.history.
+ Backbone.history = new History;
- // Mix in each Underscore method as a proxy to `Collection#models`.
- addUnderscoreMethods(Collection, collectionMethods, 'models');
+ // Helpers
+ // -------
- // Backbone.View
- // -------------
+ // Helper function to correctly set up the prototype chain for subclasses.
+ // Similar to `goog.inherits`, but uses a hash of prototype properties and
+ // class properties to be extended.
+ var extend = function(protoProps, staticProps) {
+ var parent = this;
+ var child;
- // Backbone Views are almost more convention than they are actual code. A View
- // is simply a JavaScript object that represents a logical chunk of UI in the
- // DOM. This might be a single item, an entire list, a sidebar or panel, or
- // even the surrounding frame which wraps your whole app. Defining a chunk of
- // UI as a **View** allows you to define your DOM events declaratively, without
- // having to worry about render order ... and makes it easy for the view to
- // react to specific changes in the state of your models.
+ // The constructor function for the new subclass is either defined by you
+ // (the "constructor" property in your `extend` definition), or defaulted
+ // by us to simply call the parent constructor.
+ if (protoProps && _.has(protoProps, 'constructor')) {
+ child = protoProps.constructor;
+ } else {
+ child = function(){ return parent.apply(this, arguments); };
+ }
- // Creating a Backbone.View creates its initial element outside of the DOM,
- // if an existing element is not provided...
- var View = Backbone.View = function(options) {
- this.cid = _.uniqueId('view');
- _.extend(this, _.pick(options, viewOptions));
- this._ensureElement();
- this.initialize.apply(this, arguments);
+ // Add static properties to the constructor function, if supplied.
+ _.extend(child, parent, staticProps);
+
+ // Set the prototype chain to inherit from `parent`, without calling
+ // `parent` constructor function.
+ var Surrogate = function(){ this.constructor = child; };
+ Surrogate.prototype = parent.prototype;
+ child.prototype = new Surrogate;
+
+ // Add prototype properties (instance properties) to the subclass,
+ // if supplied.
+ if (protoProps) _.extend(child.prototype, protoProps);
+
+ // Set a convenience property in case the parent's prototype is needed
+ // later.
+ child.__super__ = parent.prototype;
+
+ return child;
};
- // Cached regex to split keys for `delegate`.
- var delegateEventSplitter = /^(\S+)\s*(.*)$/;
+ // Set up inheritance for the model, collection, router, view and history.
+ Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
- // List of view options to be set as properties.
- var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
+ // Throw an error when a URL is needed, and none is supplied.
+ var urlError = function() {
+ throw new Error('A "url" property or function must be specified');
+ };
- // Set up all inheritable **Backbone.View** properties and methods.
- _.extend(View.prototype, Events, {
+ // Wrap an optional error callback with a fallback error event.
+ var wrapError = function(model, options) {
+ var error = options.error;
+ options.error = function(resp) {
+ if (error) error.call(options.context, model, resp, options);
+ model.trigger('error', model, resp, options);
+ };
+ };
- // The default `tagName` of a View's element is `"div"`.
- tagName: 'div',
+ return Backbone;
- // jQuery delegate for element lookup, scoped to DOM elements within the
- // current view. This should be preferred to global lookups where possible.
- $: function(selector) {
- return this.$el.find(selector);
- },
+}));
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize: function(){},
+/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
- // **render** is the core function that your view should override, in order
- // to populate its element (`this.el`), with the appropriate HTML. The
- // convention is for **render** to always return `this`.
- render: function() {
- return this;
- },
+/***/ }),
- // Remove this view by taking the element out of the DOM, and removing any
- // applicable Backbone.Events listeners.
- remove: function() {
- this._removeElement();
- this.stopListening();
- return this;
- },
+/***/ "./node_modules/backbone-undo/node_modules/underscore/underscore.js":
+/*!**************************************************************************!*\
+ !*** ./node_modules/backbone-undo/node_modules/underscore/underscore.js ***!
+ \**************************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
- // Remove this view's element from the document and all event listeners
- // attached to it. Exposed for subclasses using an alternative DOM
- // manipulation API.
- _removeElement: function() {
- this.$el.remove();
- },
+var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Underscore.js 1.8.3
+// http://underscorejs.org
+// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+// Underscore may be freely distributed under the MIT license.
- // Change the view's element (`this.el` property) and re-delegate the
- // view's events on the new element.
- setElement: function(element) {
- this.undelegateEvents();
- this._setElement(element);
- this.delegateEvents();
- return this;
- },
+(function() {
- // Creates the `this.el` and `this.$el` references for this view using the
- // given `el`. `el` can be a CSS selector or an HTML string, a jQuery
- // context or an element. Subclasses can override this to utilize an
- // alternative DOM manipulation API and are only required to set the
- // `this.el` property.
- _setElement: function(el) {
- this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
- this.el = this.$el[0];
- },
+ // Baseline setup
+ // --------------
- // Set callbacks, where `this.events` is a hash of
- //
- // *{"event selector": "callback"}*
- //
- // {
- // 'mousedown .title': 'edit',
- // 'click .button': 'save',
- // 'click .open': function(e) { ... }
- // }
- //
- // pairs. Callbacks will be bound to the view, with `this` set properly.
- // Uses event delegation for efficiency.
- // Omitting the selector binds the event to `this.el`.
- delegateEvents: function(events) {
- events || (events = _.result(this, 'events'));
- if (!events) return this;
- this.undelegateEvents();
- for (var key in events) {
- var method = events[key];
- if (!_.isFunction(method)) method = this[method];
- if (!method) continue;
- var match = key.match(delegateEventSplitter);
- this.delegate(match[1], match[2], _.bind(method, this));
- }
- return this;
- },
+ // Establish the root object, `window` in the browser, or `exports` on the server.
+ var root = this;
- // Add a single event listener to the view's element (or a child element
- // using `selector`). This only works for delegate-able events: not `focus`,
- // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
- delegate: function(eventName, selector, listener) {
- this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener);
- return this;
- },
+ // Save the previous value of the `_` variable.
+ var previousUnderscore = root._;
- // Clears all callbacks previously bound to the view by `delegateEvents`.
- // You usually don't need to use this, but may wish to if you have multiple
- // Backbone views attached to the same DOM element.
- undelegateEvents: function() {
- if (this.$el) this.$el.off('.delegateEvents' + this.cid);
- return this;
- },
+ // Save bytes in the minified (but not gzipped) version:
+ var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
- // A finer-grained `undelegateEvents` for removing a single delegated event.
- // `selector` and `listener` are both optional.
- undelegate: function(eventName, selector, listener) {
- this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener);
- return this;
- },
+ // Create quick reference variables for speed access to core prototypes.
+ var
+ push = ArrayProto.push,
+ slice = ArrayProto.slice,
+ toString = ObjProto.toString,
+ hasOwnProperty = ObjProto.hasOwnProperty;
- // Produces a DOM element to be assigned to your view. Exposed for
- // subclasses using an alternative DOM manipulation API.
- _createElement: function(tagName) {
- return document.createElement(tagName);
- },
+ // All **ECMAScript 5** native function implementations that we hope to use
+ // are declared here.
+ var
+ nativeIsArray = Array.isArray,
+ nativeKeys = Object.keys,
+ nativeBind = FuncProto.bind,
+ nativeCreate = Object.create;
- // Ensure that the View has a DOM element to render into.
- // If `this.el` is a string, pass it through `$()`, take the first
- // matching element, and re-assign it to `el`. Otherwise, create
- // an element from the `id`, `className` and `tagName` properties.
- _ensureElement: function() {
- if (!this.el) {
- var attrs = _.extend({}, _.result(this, 'attributes'));
- if (this.id) attrs.id = _.result(this, 'id');
- if (this.className) attrs['class'] = _.result(this, 'className');
- this.setElement(this._createElement(_.result(this, 'tagName')));
- this._setAttributes(attrs);
- } else {
- this.setElement(_.result(this, 'el'));
- }
- },
+ // Naked function reference for surrogate-prototype-swapping.
+ var Ctor = function(){};
- // Set attributes from a hash on this view's element. Exposed for
- // subclasses using an alternative DOM manipulation API.
- _setAttributes: function(attributes) {
- this.$el.attr(attributes);
+ // Create a safe reference to the Underscore object for use below.
+ var _ = function(obj) {
+ if (obj instanceof _) return obj;
+ if (!(this instanceof _)) return new _(obj);
+ this._wrapped = obj;
+ };
+
+ // Export the Underscore object for **Node.js**, with
+ // backwards-compatibility for the old `require()` API. If we're in
+ // the browser, add `_` as a global object.
+ if (true) {
+ if (typeof module !== 'undefined' && module.exports) {
+ exports = module.exports = _;
}
+ exports._ = _;
+ } else {}
- });
+ // Current version.
+ _.VERSION = '1.8.3';
- // Backbone.sync
- // -------------
+ // Internal function that returns an efficient (for current engines) version
+ // of the passed-in callback, to be repeatedly applied in other Underscore
+ // functions.
+ var optimizeCb = function(func, context, argCount) {
+ if (context === void 0) return func;
+ switch (argCount == null ? 3 : argCount) {
+ case 1: return function(value) {
+ return func.call(context, value);
+ };
+ case 2: return function(value, other) {
+ return func.call(context, value, other);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(context, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(context, accumulator, value, index, collection);
+ };
+ }
+ return function() {
+ return func.apply(context, arguments);
+ };
+ };
- // Override this function to change the manner in which Backbone persists
- // models to the server. You will be passed the type of request, and the
- // model in question. By default, makes a RESTful Ajax request
- // to the model's `url()`. Some possible customizations could be:
- //
- // * Use `setTimeout` to batch rapid-fire updates into a single request.
- // * Send up the models as XML instead of JSON.
- // * Persist models via WebSockets instead of Ajax.
- //
- // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
- // as `POST`, with a `_method` parameter containing the true HTTP method,
- // as well as all requests with the body as `application/x-www-form-urlencoded`
- // instead of `application/json` with the model in a param named `model`.
- // Useful when interfacing with server-side languages like **PHP** that make
- // it difficult to read the body of `PUT` requests.
- Backbone.sync = function(method, model, options) {
- var type = methodMap[method];
+ // A mostly-internal function to generate callbacks that can be applied
+ // to each element in a collection, returning the desired result — either
+ // identity, an arbitrary callback, a property matcher, or a property accessor.
+ var cb = function(value, context, argCount) {
+ if (value == null) return _.identity;
+ if (_.isFunction(value)) return optimizeCb(value, context, argCount);
+ if (_.isObject(value)) return _.matcher(value);
+ return _.property(value);
+ };
+ _.iteratee = function(value, context) {
+ return cb(value, context, Infinity);
+ };
+
+ // An internal function for creating assigner functions.
+ var createAssigner = function(keysFunc, undefinedOnly) {
+ return function(obj) {
+ var length = arguments.length;
+ if (length < 2 || obj == null) return obj;
+ for (var index = 1; index < length; index++) {
+ var source = arguments[index],
+ keys = keysFunc(source),
+ l = keys.length;
+ for (var i = 0; i < l; i++) {
+ var key = keys[i];
+ if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
+ }
+ }
+ return obj;
+ };
+ };
- // Default options, unless specified.
- _.defaults(options || (options = {}), {
- emulateHTTP: Backbone.emulateHTTP,
- emulateJSON: Backbone.emulateJSON
- });
+ // An internal function for creating a new object that inherits from another.
+ var baseCreate = function(prototype) {
+ if (!_.isObject(prototype)) return {};
+ if (nativeCreate) return nativeCreate(prototype);
+ Ctor.prototype = prototype;
+ var result = new Ctor;
+ Ctor.prototype = null;
+ return result;
+ };
- // Default JSON-request options.
- var params = {type: type, dataType: 'json'};
+ var property = function(key) {
+ return function(obj) {
+ return obj == null ? void 0 : obj[key];
+ };
+ };
- // Ensure that we have a URL.
- if (!options.url) {
- params.url = _.result(model, 'url') || urlError();
- }
+ // Helper for collection methods to determine whether a collection
+ // should be iterated as an array or as an object
+ // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
+ // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
+ var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
+ var getLength = property('length');
+ var isArrayLike = function(collection) {
+ var length = getLength(collection);
+ return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
+ };
- // Ensure that we have the appropriate request data.
- if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
- params.contentType = 'application/json';
- params.data = JSON.stringify(options.attrs || model.toJSON(options));
- }
+ // Collection Functions
+ // --------------------
- // For older servers, emulate JSON by encoding the request into an HTML-form.
- if (options.emulateJSON) {
- params.contentType = 'application/x-www-form-urlencoded';
- params.data = params.data ? {model: params.data} : {};
+ // The cornerstone, an `each` implementation, aka `forEach`.
+ // Handles raw objects in addition to array-likes. Treats all
+ // sparse array-likes as if they were dense.
+ _.each = _.forEach = function(obj, iteratee, context) {
+ iteratee = optimizeCb(iteratee, context);
+ var i, length;
+ if (isArrayLike(obj)) {
+ for (i = 0, length = obj.length; i < length; i++) {
+ iteratee(obj[i], i, obj);
+ }
+ } else {
+ var keys = _.keys(obj);
+ for (i = 0, length = keys.length; i < length; i++) {
+ iteratee(obj[keys[i]], keys[i], obj);
+ }
}
+ return obj;
+ };
- // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
- // And an `X-HTTP-Method-Override` header.
- if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
- params.type = 'POST';
- if (options.emulateJSON) params.data._method = type;
- var beforeSend = options.beforeSend;
- options.beforeSend = function(xhr) {
- xhr.setRequestHeader('X-HTTP-Method-Override', type);
- if (beforeSend) return beforeSend.apply(this, arguments);
- };
+ // Return the results of applying the iteratee to each element.
+ _.map = _.collect = function(obj, iteratee, context) {
+ iteratee = cb(iteratee, context);
+ var keys = !isArrayLike(obj) && _.keys(obj),
+ length = (keys || obj).length,
+ results = Array(length);
+ for (var index = 0; index < length; index++) {
+ var currentKey = keys ? keys[index] : index;
+ results[index] = iteratee(obj[currentKey], currentKey, obj);
}
+ return results;
+ };
- // Don't process data on a non-GET request.
- if (params.type !== 'GET' && !options.emulateJSON) {
- params.processData = false;
+ // Create a reducing function iterating left or right.
+ function createReduce(dir) {
+ // Optimized iterator function as using arguments.length
+ // in the main function will deoptimize the, see #1991.
+ function iterator(obj, iteratee, memo, keys, index, length) {
+ for (; index >= 0 && index < length; index += dir) {
+ var currentKey = keys ? keys[index] : index;
+ memo = iteratee(memo, obj[currentKey], currentKey, obj);
+ }
+ return memo;
}
- // Pass along `textStatus` and `errorThrown` from jQuery.
- var error = options.error;
- options.error = function(xhr, textStatus, errorThrown) {
- options.textStatus = textStatus;
- options.errorThrown = errorThrown;
- if (error) error.call(options.context, xhr, textStatus, errorThrown);
+ return function(obj, iteratee, memo, context) {
+ iteratee = optimizeCb(iteratee, context, 4);
+ var keys = !isArrayLike(obj) && _.keys(obj),
+ length = (keys || obj).length,
+ index = dir > 0 ? 0 : length - 1;
+ // Determine the initial value if none is provided.
+ if (arguments.length < 3) {
+ memo = obj[keys ? keys[index] : index];
+ index += dir;
+ }
+ return iterator(obj, iteratee, memo, keys, index, length);
};
+ }
- // Make the request, allowing the user to override any Ajax options.
- var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
- model.trigger('request', model, xhr, options);
- return xhr;
+ // **Reduce** builds up a single result from a list of values, aka `inject`,
+ // or `foldl`.
+ _.reduce = _.foldl = _.inject = createReduce(1);
+
+ // The right-associative version of reduce, also known as `foldr`.
+ _.reduceRight = _.foldr = createReduce(-1);
+
+ // Return the first value which passes a truth test. Aliased as `detect`.
+ _.find = _.detect = function(obj, predicate, context) {
+ var key;
+ if (isArrayLike(obj)) {
+ key = _.findIndex(obj, predicate, context);
+ } else {
+ key = _.findKey(obj, predicate, context);
+ }
+ if (key !== void 0 && key !== -1) return obj[key];
};
- // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
- var methodMap = {
- 'create': 'POST',
- 'update': 'PUT',
- 'patch': 'PATCH',
- 'delete': 'DELETE',
- 'read': 'GET'
+ // Return all the elements that pass a truth test.
+ // Aliased as `select`.
+ _.filter = _.select = function(obj, predicate, context) {
+ var results = [];
+ predicate = cb(predicate, context);
+ _.each(obj, function(value, index, list) {
+ if (predicate(value, index, list)) results.push(value);
+ });
+ return results;
};
- // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
- // Override this if you'd like to use a different library.
- Backbone.ajax = function() {
- return Backbone.$.ajax.apply(Backbone.$, arguments);
+ // Return all the elements for which a truth test fails.
+ _.reject = function(obj, predicate, context) {
+ return _.filter(obj, _.negate(cb(predicate)), context);
};
- // Backbone.Router
- // ---------------
+ // Determine whether all of the elements match a truth test.
+ // Aliased as `all`.
+ _.every = _.all = function(obj, predicate, context) {
+ predicate = cb(predicate, context);
+ var keys = !isArrayLike(obj) && _.keys(obj),
+ length = (keys || obj).length;
+ for (var index = 0; index < length; index++) {
+ var currentKey = keys ? keys[index] : index;
+ if (!predicate(obj[currentKey], currentKey, obj)) return false;
+ }
+ return true;
+ };
- // Routers map faux-URLs to actions, and fire events when routes are
- // matched. Creating a new one sets its `routes` hash, if not set statically.
- var Router = Backbone.Router = function(options) {
- options || (options = {});
- if (options.routes) this.routes = options.routes;
- this._bindRoutes();
- this.initialize.apply(this, arguments);
+ // Determine if at least one element in the object matches a truth test.
+ // Aliased as `any`.
+ _.some = _.any = function(obj, predicate, context) {
+ predicate = cb(predicate, context);
+ var keys = !isArrayLike(obj) && _.keys(obj),
+ length = (keys || obj).length;
+ for (var index = 0; index < length; index++) {
+ var currentKey = keys ? keys[index] : index;
+ if (predicate(obj[currentKey], currentKey, obj)) return true;
+ }
+ return false;
};
- // Cached regular expressions for matching named param parts and splatted
- // parts of route strings.
- var optionalParam = /\((.*?)\)/g;
- var namedParam = /(\(\?)?:\w+/g;
- var splatParam = /\*\w+/g;
- var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
+ // Determine if the array or object contains a given item (using `===`).
+ // Aliased as `includes` and `include`.
+ _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
+ if (!isArrayLike(obj)) obj = _.values(obj);
+ if (typeof fromIndex != 'number' || guard) fromIndex = 0;
+ return _.indexOf(obj, item, fromIndex) >= 0;
+ };
- // Set up all inheritable **Backbone.Router** properties and methods.
- _.extend(Router.prototype, Events, {
+ // Invoke a method (with arguments) on every item in a collection.
+ _.invoke = function(obj, method) {
+ var args = slice.call(arguments, 2);
+ var isFunc = _.isFunction(method);
+ return _.map(obj, function(value) {
+ var func = isFunc ? method : value[method];
+ return func == null ? func : func.apply(value, args);
+ });
+ };
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize: function(){},
+ // Convenience version of a common use case of `map`: fetching a property.
+ _.pluck = function(obj, key) {
+ return _.map(obj, _.property(key));
+ };
- // Manually bind a single named route to a callback. For example:
- //
- // this.route('search/:query/p:num', 'search', function(query, num) {
- // ...
- // });
- //
- route: function(route, name, callback) {
- if (!_.isRegExp(route)) route = this._routeToRegExp(route);
- if (_.isFunction(name)) {
- callback = name;
- name = '';
+ // Convenience version of a common use case of `filter`: selecting only objects
+ // containing specific `key:value` pairs.
+ _.where = function(obj, attrs) {
+ return _.filter(obj, _.matcher(attrs));
+ };
+
+ // Convenience version of a common use case of `find`: getting the first object
+ // containing specific `key:value` pairs.
+ _.findWhere = function(obj, attrs) {
+ return _.find(obj, _.matcher(attrs));
+ };
+
+ // Return the maximum element (or element-based computation).
+ _.max = function(obj, iteratee, context) {
+ var result = -Infinity, lastComputed = -Infinity,
+ value, computed;
+ if (iteratee == null && obj != null) {
+ obj = isArrayLike(obj) ? obj : _.values(obj);
+ for (var i = 0, length = obj.length; i < length; i++) {
+ value = obj[i];
+ if (value > result) {
+ result = value;
+ }
}
- if (!callback) callback = this[name];
- var router = this;
- Backbone.history.route(route, function(fragment) {
- var args = router._extractParameters(route, fragment);
- if (router.execute(callback, args, name) !== false) {
- router.trigger.apply(router, ['route:' + name].concat(args));
- router.trigger('route', name, args);
- Backbone.history.trigger('route', router, name, args);
+ } else {
+ iteratee = cb(iteratee, context);
+ _.each(obj, function(value, index, list) {
+ computed = iteratee(value, index, list);
+ if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
+ result = value;
+ lastComputed = computed;
}
});
- return this;
- },
-
- // Execute a route handler with the provided parameters. This is an
- // excellent place to do pre-route setup or post-route cleanup.
- execute: function(callback, args, name) {
- if (callback) callback.apply(this, args);
- },
-
- // Simple proxy to `Backbone.history` to save a fragment into the history.
- navigate: function(fragment, options) {
- Backbone.history.navigate(fragment, options);
- return this;
- },
+ }
+ return result;
+ };
- // Bind all defined routes to `Backbone.history`. We have to reverse the
- // order of the routes here to support behavior where the most general
- // routes can be defined at the bottom of the route map.
- _bindRoutes: function() {
- if (!this.routes) return;
- this.routes = _.result(this, 'routes');
- var route, routes = _.keys(this.routes);
- while ((route = routes.pop()) != null) {
- this.route(route, this.routes[route]);
+ // Return the minimum element (or element-based computation).
+ _.min = function(obj, iteratee, context) {
+ var result = Infinity, lastComputed = Infinity,
+ value, computed;
+ if (iteratee == null && obj != null) {
+ obj = isArrayLike(obj) ? obj : _.values(obj);
+ for (var i = 0, length = obj.length; i < length; i++) {
+ value = obj[i];
+ if (value < result) {
+ result = value;
+ }
}
- },
-
- // Convert a route string into a regular expression, suitable for matching
- // against the current location hash.
- _routeToRegExp: function(route) {
- route = route.replace(escapeRegExp, '\\$&')
- .replace(optionalParam, '(?:$1)?')
- .replace(namedParam, function(match, optional) {
- return optional ? match : '([^/?]+)';
- })
- .replace(splatParam, '([^?]*?)');
- return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
- },
-
- // Given a route, and a URL fragment that it matches, return the array of
- // extracted decoded parameters. Empty or unmatched parameters will be
- // treated as `null` to normalize cross-browser behavior.
- _extractParameters: function(route, fragment) {
- var params = route.exec(fragment).slice(1);
- return _.map(params, function(param, i) {
- // Don't decode the search params.
- if (i === params.length - 1) return param || null;
- return param ? decodeURIComponent(param) : null;
+ } else {
+ iteratee = cb(iteratee, context);
+ _.each(obj, function(value, index, list) {
+ computed = iteratee(value, index, list);
+ if (computed < lastComputed || computed === Infinity && result === Infinity) {
+ result = value;
+ lastComputed = computed;
+ }
});
}
+ return result;
+ };
- });
-
- // Backbone.History
- // ----------------
-
- // Handles cross-browser history management, based on either
- // [pushState](http://diveintohtml5.info/history.html) and real URLs, or
- // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
- // and URL fragments. If the browser supports neither (old IE, natch),
- // falls back to polling.
- var History = Backbone.History = function() {
- this.handlers = [];
- this.checkUrl = _.bind(this.checkUrl, this);
-
- // Ensure that `History` can be used outside of the browser.
- if (typeof window !== 'undefined') {
- this.location = window.location;
- this.history = window.history;
+ // Shuffle a collection, using the modern version of the
+ // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
+ _.shuffle = function(obj) {
+ var set = isArrayLike(obj) ? obj : _.values(obj);
+ var length = set.length;
+ var shuffled = Array(length);
+ for (var index = 0, rand; index < length; index++) {
+ rand = _.random(0, index);
+ if (rand !== index) shuffled[index] = shuffled[rand];
+ shuffled[rand] = set[index];
}
+ return shuffled;
};
- // Cached regex for stripping a leading hash/slash and trailing space.
- var routeStripper = /^[#\/]|\s+$/g;
-
- // Cached regex for stripping leading and trailing slashes.
- var rootStripper = /^\/+|\/+$/g;
-
- // Cached regex for stripping urls of hash.
- var pathStripper = /#.*$/;
-
- // Has the history handling already been started?
- History.started = false;
+ // Sample **n** random values from a collection.
+ // If **n** is not specified, returns a single random element.
+ // The internal `guard` argument allows it to work with `map`.
+ _.sample = function(obj, n, guard) {
+ if (n == null || guard) {
+ if (!isArrayLike(obj)) obj = _.values(obj);
+ return obj[_.random(obj.length - 1)];
+ }
+ return _.shuffle(obj).slice(0, Math.max(0, n));
+ };
- // Set up all inheritable **Backbone.History** properties and methods.
- _.extend(History.prototype, Events, {
+ // Sort the object's values by a criterion produced by an iteratee.
+ _.sortBy = function(obj, iteratee, context) {
+ iteratee = cb(iteratee, context);
+ return _.pluck(_.map(obj, function(value, index, list) {
+ return {
+ value: value,
+ index: index,
+ criteria: iteratee(value, index, list)
+ };
+ }).sort(function(left, right) {
+ var a = left.criteria;
+ var b = right.criteria;
+ if (a !== b) {
+ if (a > b || a === void 0) return 1;
+ if (a < b || b === void 0) return -1;
+ }
+ return left.index - right.index;
+ }), 'value');
+ };
- // The default interval to poll for hash changes, if necessary, is
- // twenty times a second.
- interval: 50,
+ // An internal function used for aggregate "group by" operations.
+ var group = function(behavior) {
+ return function(obj, iteratee, context) {
+ var result = {};
+ iteratee = cb(iteratee, context);
+ _.each(obj, function(value, index) {
+ var key = iteratee(value, index, obj);
+ behavior(result, value, key);
+ });
+ return result;
+ };
+ };
- // Are we at the app root?
- atRoot: function() {
- var path = this.location.pathname.replace(/[^\/]$/, '$&/');
- return path === this.root && !this.getSearch();
- },
+ // Groups the object's values by a criterion. Pass either a string attribute
+ // to group by, or a function that returns the criterion.
+ _.groupBy = group(function(result, value, key) {
+ if (_.has(result, key)) result[key].push(value); else result[key] = [value];
+ });
- // Does the pathname match the root?
- matchRoot: function() {
- var path = this.decodeFragment(this.location.pathname);
- var rootPath = path.slice(0, this.root.length - 1) + '/';
- return rootPath === this.root;
- },
+ // Indexes the object's values by a criterion, similar to `groupBy`, but for
+ // when you know that your index values will be unique.
+ _.indexBy = group(function(result, value, key) {
+ result[key] = value;
+ });
- // Unicode characters in `location.pathname` are percent encoded so they're
- // decoded for comparison. `%25` should not be decoded since it may be part
- // of an encoded parameter.
- decodeFragment: function(fragment) {
- return decodeURI(fragment.replace(/%25/g, '%2525'));
- },
+ // Counts instances of an object that group by a certain criterion. Pass
+ // either a string attribute to count by, or a function that returns the
+ // criterion.
+ _.countBy = group(function(result, value, key) {
+ if (_.has(result, key)) result[key]++; else result[key] = 1;
+ });
- // In IE6, the hash fragment and search params are incorrect if the
- // fragment contains `?`.
- getSearch: function() {
- var match = this.location.href.replace(/#.*/, '').match(/\?.+/);
- return match ? match[0] : '';
- },
+ // Safely create a real, live array from anything iterable.
+ _.toArray = function(obj) {
+ if (!obj) return [];
+ if (_.isArray(obj)) return slice.call(obj);
+ if (isArrayLike(obj)) return _.map(obj, _.identity);
+ return _.values(obj);
+ };
- // Gets the true hash value. Cannot use location.hash directly due to bug
- // in Firefox where location.hash will always be decoded.
- getHash: function(window) {
- var match = (window || this).location.href.match(/#(.*)$/);
- return match ? match[1] : '';
- },
+ // Return the number of elements in an object.
+ _.size = function(obj) {
+ if (obj == null) return 0;
+ return isArrayLike(obj) ? obj.length : _.keys(obj).length;
+ };
- // Get the pathname and search params, without the root.
- getPath: function() {
- var path = this.decodeFragment(
- this.location.pathname + this.getSearch()
- ).slice(this.root.length - 1);
- return path.charAt(0) === '/' ? path.slice(1) : path;
- },
+ // Split a collection into two arrays: one whose elements all satisfy the given
+ // predicate, and one whose elements all do not satisfy the predicate.
+ _.partition = function(obj, predicate, context) {
+ predicate = cb(predicate, context);
+ var pass = [], fail = [];
+ _.each(obj, function(value, key, obj) {
+ (predicate(value, key, obj) ? pass : fail).push(value);
+ });
+ return [pass, fail];
+ };
- // Get the cross-browser normalized URL fragment from the path or hash.
- getFragment: function(fragment) {
- if (fragment == null) {
- if (this._usePushState || !this._wantsHashChange) {
- fragment = this.getPath();
- } else {
- fragment = this.getHash();
- }
- }
- return fragment.replace(routeStripper, '');
- },
+ // Array Functions
+ // ---------------
- // Start the hash change handling, returning `true` if the current URL matches
- // an existing route, and `false` otherwise.
- start: function(options) {
- if (History.started) throw new Error('Backbone.history has already been started');
- History.started = true;
+ // Get the first element of an array. Passing **n** will return the first N
+ // values in the array. Aliased as `head` and `take`. The **guard** check
+ // allows it to work with `_.map`.
+ _.first = _.head = _.take = function(array, n, guard) {
+ if (array == null) return void 0;
+ if (n == null || guard) return array[0];
+ return _.initial(array, array.length - n);
+ };
- // Figure out the initial configuration. Do we need an iframe?
- // Is pushState desired ... is it available?
- this.options = _.extend({root: '/'}, this.options, options);
- this.root = this.options.root;
- this._wantsHashChange = this.options.hashChange !== false;
- this._hasHashChange = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7);
- this._useHashChange = this._wantsHashChange && this._hasHashChange;
- this._wantsPushState = !!this.options.pushState;
- this._hasPushState = !!(this.history && this.history.pushState);
- this._usePushState = this._wantsPushState && this._hasPushState;
- this.fragment = this.getFragment();
+ // Returns everything but the last entry of the array. Especially useful on
+ // the arguments object. Passing **n** will return all the values in
+ // the array, excluding the last N.
+ _.initial = function(array, n, guard) {
+ return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
+ };
- // Normalize root to always include a leading and trailing slash.
- this.root = ('/' + this.root + '/').replace(rootStripper, '/');
+ // Get the last element of an array. Passing **n** will return the last N
+ // values in the array.
+ _.last = function(array, n, guard) {
+ if (array == null) return void 0;
+ if (n == null || guard) return array[array.length - 1];
+ return _.rest(array, Math.max(0, array.length - n));
+ };
- // Transition from hashChange to pushState or vice versa if both are
- // requested.
- if (this._wantsHashChange && this._wantsPushState) {
+ // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
+ // Especially useful on the arguments object. Passing an **n** will return
+ // the rest N values in the array.
+ _.rest = _.tail = _.drop = function(array, n, guard) {
+ return slice.call(array, n == null || guard ? 1 : n);
+ };
- // If we've started off with a route from a `pushState`-enabled
- // browser, but we're currently in a browser that doesn't support it...
- if (!this._hasPushState && !this.atRoot()) {
- var rootPath = this.root.slice(0, -1) || '/';
- this.location.replace(rootPath + '#' + this.getPath());
- // Return immediately as browser will do redirect to new url
- return true;
+ // Trim out all falsy values from an array.
+ _.compact = function(array) {
+ return _.filter(array, _.identity);
+ };
- // Or if we've started out with a hash-based route, but we're currently
- // in a browser where it could be `pushState`-based instead...
- } else if (this._hasPushState && this.atRoot()) {
- this.navigate(this.getHash(), {replace: true});
+ // Internal implementation of a recursive `flatten` function.
+ var flatten = function(input, shallow, strict, startIndex) {
+ var output = [], idx = 0;
+ for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
+ var value = input[i];
+ if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
+ //flatten current level of array or arguments object
+ if (!shallow) value = flatten(value, shallow, strict);
+ var j = 0, len = value.length;
+ output.length += len;
+ while (j < len) {
+ output[idx++] = value[j++];
}
-
+ } else if (!strict) {
+ output[idx++] = value;
}
+ }
+ return output;
+ };
- // Proxy an iframe to handle location events if the browser doesn't
- // support the `hashchange` event, HTML5 history, or the user wants
- // `hashChange` but not `pushState`.
- if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) {
- this.iframe = document.createElement('iframe');
- this.iframe.src = 'javascript:0';
- this.iframe.style.display = 'none';
- this.iframe.tabIndex = -1;
- var body = document.body;
- // Using `appendChild` will throw on IE < 9 if the document is not ready.
- var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow;
- iWindow.document.open();
- iWindow.document.close();
- iWindow.location.hash = '#' + this.fragment;
- }
+ // Flatten out an array, either recursively (by default), or just one level.
+ _.flatten = function(array, shallow) {
+ return flatten(array, shallow, false);
+ };
- // Add a cross-platform `addEventListener` shim for older browsers.
- var addEventListener = window.addEventListener || function(eventName, listener) {
- return attachEvent('on' + eventName, listener);
- };
+ // Return a version of the array that does not contain the specified value(s).
+ _.without = function(array) {
+ return _.difference(array, slice.call(arguments, 1));
+ };
- // Depending on whether we're using pushState or hashes, and whether
- // 'onhashchange' is supported, determine how we check the URL state.
- if (this._usePushState) {
- addEventListener('popstate', this.checkUrl, false);
- } else if (this._useHashChange && !this.iframe) {
- addEventListener('hashchange', this.checkUrl, false);
- } else if (this._wantsHashChange) {
- this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
+ // Produce a duplicate-free version of the array. If the array has already
+ // been sorted, you have the option of using a faster algorithm.
+ // Aliased as `unique`.
+ _.uniq = _.unique = function(array, isSorted, iteratee, context) {
+ if (!_.isBoolean(isSorted)) {
+ context = iteratee;
+ iteratee = isSorted;
+ isSorted = false;
+ }
+ if (iteratee != null) iteratee = cb(iteratee, context);
+ var result = [];
+ var seen = [];
+ for (var i = 0, length = getLength(array); i < length; i++) {
+ var value = array[i],
+ computed = iteratee ? iteratee(value, i, array) : value;
+ if (isSorted) {
+ if (!i || seen !== computed) result.push(value);
+ seen = computed;
+ } else if (iteratee) {
+ if (!_.contains(seen, computed)) {
+ seen.push(computed);
+ result.push(value);
+ }
+ } else if (!_.contains(result, value)) {
+ result.push(value);
}
+ }
+ return result;
+ };
- if (!this.options.silent) return this.loadUrl();
- },
-
- // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
- // but possibly useful for unit testing Routers.
- stop: function() {
- // Add a cross-platform `removeEventListener` shim for older browsers.
- var removeEventListener = window.removeEventListener || function(eventName, listener) {
- return detachEvent('on' + eventName, listener);
- };
+ // Produce an array that contains the union: each distinct element from all of
+ // the passed-in arrays.
+ _.union = function() {
+ return _.uniq(flatten(arguments, true, true));
+ };
- // Remove window listeners.
- if (this._usePushState) {
- removeEventListener('popstate', this.checkUrl, false);
- } else if (this._useHashChange && !this.iframe) {
- removeEventListener('hashchange', this.checkUrl, false);
+ // Produce an array that contains every item shared between all the
+ // passed-in arrays.
+ _.intersection = function(array) {
+ var result = [];
+ var argsLength = arguments.length;
+ for (var i = 0, length = getLength(array); i < length; i++) {
+ var item = array[i];
+ if (_.contains(result, item)) continue;
+ for (var j = 1; j < argsLength; j++) {
+ if (!_.contains(arguments[j], item)) break;
}
+ if (j === argsLength) result.push(item);
+ }
+ return result;
+ };
- // Clean up the iframe if necessary.
- if (this.iframe) {
- document.body.removeChild(this.iframe);
- this.iframe = null;
- }
+ // Take the difference between one array and a number of other arrays.
+ // Only the elements present in just the first array will remain.
+ _.difference = function(array) {
+ var rest = flatten(arguments, true, true, 1);
+ return _.filter(array, function(value){
+ return !_.contains(rest, value);
+ });
+ };
- // Some environments will throw when clearing an undefined interval.
- if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
- History.started = false;
- },
+ // Zip together multiple lists into a single array -- elements that share
+ // an index go together.
+ _.zip = function() {
+ return _.unzip(arguments);
+ };
- // Add a route to be tested when the fragment changes. Routes added later
- // may override previous routes.
- route: function(route, callback) {
- this.handlers.unshift({route: route, callback: callback});
- },
+ // Complement of _.zip. Unzip accepts an array of arrays and groups
+ // each array's elements on shared indices
+ _.unzip = function(array) {
+ var length = array && _.max(array, getLength).length || 0;
+ var result = Array(length);
- // Checks the current URL to see if it has changed, and if it has,
- // calls `loadUrl`, normalizing across the hidden iframe.
- checkUrl: function(e) {
- var current = this.getFragment();
+ for (var index = 0; index < length; index++) {
+ result[index] = _.pluck(array, index);
+ }
+ return result;
+ };
- // If the user pressed the back button, the iframe's hash will have
- // changed and we should use that for comparison.
- if (current === this.fragment && this.iframe) {
- current = this.getHash(this.iframe.contentWindow);
+ // Converts lists into objects. Pass either a single array of `[key, value]`
+ // pairs, or two parallel arrays of the same length -- one of keys, and one of
+ // the corresponding values.
+ _.object = function(list, values) {
+ var result = {};
+ for (var i = 0, length = getLength(list); i < length; i++) {
+ if (values) {
+ result[list[i]] = values[i];
+ } else {
+ result[list[i][0]] = list[i][1];
}
+ }
+ return result;
+ };
- if (current === this.fragment) return false;
- if (this.iframe) this.navigate(current);
- this.loadUrl();
- },
-
- // Attempt to load the current URL fragment. If a route succeeds with a
- // match, returns `true`. If no defined routes matches the fragment,
- // returns `false`.
- loadUrl: function(fragment) {
- // If the root doesn't match, no routes can match either.
- if (!this.matchRoot()) return false;
- fragment = this.fragment = this.getFragment(fragment);
- return _.some(this.handlers, function(handler) {
- if (handler.route.test(fragment)) {
- handler.callback(fragment);
- return true;
- }
- });
- },
+ // Generator function to create the findIndex and findLastIndex functions
+ function createPredicateIndexFinder(dir) {
+ return function(array, predicate, context) {
+ predicate = cb(predicate, context);
+ var length = getLength(array);
+ var index = dir > 0 ? 0 : length - 1;
+ for (; index >= 0 && index < length; index += dir) {
+ if (predicate(array[index], index, array)) return index;
+ }
+ return -1;
+ };
+ }
- // Save a fragment into the hash history, or replace the URL state if the
- // 'replace' option is passed. You are responsible for properly URL-encoding
- // the fragment in advance.
- //
- // The options object can contain `trigger: true` if you wish to have the
- // route callback be fired (not usually desirable), or `replace: true`, if
- // you wish to modify the current URL without adding an entry to the history.
- navigate: function(fragment, options) {
- if (!History.started) return false;
- if (!options || options === true) options = {trigger: !!options};
+ // Returns the first index on an array-like that passes a predicate test
+ _.findIndex = createPredicateIndexFinder(1);
+ _.findLastIndex = createPredicateIndexFinder(-1);
- // Normalize the fragment.
- fragment = this.getFragment(fragment || '');
+ // Use a comparator function to figure out the smallest index at which
+ // an object should be inserted so as to maintain order. Uses binary search.
+ _.sortedIndex = function(array, obj, iteratee, context) {
+ iteratee = cb(iteratee, context, 1);
+ var value = iteratee(obj);
+ var low = 0, high = getLength(array);
+ while (low < high) {
+ var mid = Math.floor((low + high) / 2);
+ if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
+ }
+ return low;
+ };
- // Don't include a trailing slash on the root.
- var rootPath = this.root;
- if (fragment === '' || fragment.charAt(0) === '?') {
- rootPath = rootPath.slice(0, -1) || '/';
+ // Generator function to create the indexOf and lastIndexOf functions
+ function createIndexFinder(dir, predicateFind, sortedIndex) {
+ return function(array, item, idx) {
+ var i = 0, length = getLength(array);
+ if (typeof idx == 'number') {
+ if (dir > 0) {
+ i = idx >= 0 ? idx : Math.max(idx + length, i);
+ } else {
+ length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
+ }
+ } else if (sortedIndex && idx && length) {
+ idx = sortedIndex(array, item);
+ return array[idx] === item ? idx : -1;
}
- var url = rootPath + fragment;
+ if (item !== item) {
+ idx = predicateFind(slice.call(array, i, length), _.isNaN);
+ return idx >= 0 ? idx + i : -1;
+ }
+ for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
+ if (array[idx] === item) return idx;
+ }
+ return -1;
+ };
+ }
- // Strip the hash and decode for matching.
- fragment = this.decodeFragment(fragment.replace(pathStripper, ''));
+ // Return the position of the first occurrence of an item in an array,
+ // or -1 if the item is not included in the array.
+ // If the array is large and already in sort order, pass `true`
+ // for **isSorted** to use binary search.
+ _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
+ _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
- if (this.fragment === fragment) return;
- this.fragment = fragment;
+ // Generate an integer Array containing an arithmetic progression. A port of
+ // the native Python `range()` function. See
+ // [the Python documentation](http://docs.python.org/library/functions.html#range).
+ _.range = function(start, stop, step) {
+ if (stop == null) {
+ stop = start || 0;
+ start = 0;
+ }
+ step = step || 1;
- // If pushState is available, we use it to set the fragment as a real URL.
- if (this._usePushState) {
- this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
+ var length = Math.max(Math.ceil((stop - start) / step), 0);
+ var range = Array(length);
- // If hash changes haven't been explicitly disabled, update the hash
- // fragment to store history.
- } else if (this._wantsHashChange) {
- this._updateHash(this.location, fragment, options.replace);
- if (this.iframe && fragment !== this.getHash(this.iframe.contentWindow)) {
- var iWindow = this.iframe.contentWindow;
+ for (var idx = 0; idx < length; idx++, start += step) {
+ range[idx] = start;
+ }
- // Opening and closing the iframe tricks IE7 and earlier to push a
- // history entry on hash-tag change. When replace is true, we don't
- // want this.
- if (!options.replace) {
- iWindow.document.open();
- iWindow.document.close();
- }
+ return range;
+ };
- this._updateHash(iWindow.location, fragment, options.replace);
- }
+ // Function (ahem) Functions
+ // ------------------
- // If you've told us that you explicitly don't want fallback hashchange-
- // based history, then `navigate` becomes a page refresh.
- } else {
- return this.location.assign(url);
- }
- if (options.trigger) return this.loadUrl(fragment);
- },
+ // Determines whether to execute a function as a constructor
+ // or a normal function with the provided arguments
+ var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
+ if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
+ var self = baseCreate(sourceFunc.prototype);
+ var result = sourceFunc.apply(self, args);
+ if (_.isObject(result)) return result;
+ return self;
+ };
- // Update the hash location, either replacing the current entry, or adding
- // a new one to the browser history.
- _updateHash: function(location, fragment, replace) {
- if (replace) {
- var href = location.href.replace(/(javascript:|#).*$/, '');
- location.replace(href + '#' + fragment);
- } else {
- // Some browsers require that `hash` contains a leading #.
- location.hash = '#' + fragment;
+ // Create a function bound to a given object (assigning `this`, and arguments,
+ // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
+ // available.
+ _.bind = function(func, context) {
+ if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+ if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
+ var args = slice.call(arguments, 2);
+ var bound = function() {
+ return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
+ };
+ return bound;
+ };
+
+ // Partially apply a function by creating a version that has had some of its
+ // arguments pre-filled, without changing its dynamic `this` context. _ acts
+ // as a placeholder, allowing any combination of arguments to be pre-filled.
+ _.partial = function(func) {
+ var boundArgs = slice.call(arguments, 1);
+ var bound = function() {
+ var position = 0, length = boundArgs.length;
+ var args = Array(length);
+ for (var i = 0; i < length; i++) {
+ args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
}
+ while (position < arguments.length) args.push(arguments[position++]);
+ return executeBound(func, bound, this, this, args);
+ };
+ return bound;
+ };
+
+ // Bind a number of an object's methods to that object. Remaining arguments
+ // are the method names to be bound. Useful for ensuring that all callbacks
+ // defined on an object belong to it.
+ _.bindAll = function(obj) {
+ var i, length = arguments.length, key;
+ if (length <= 1) throw new Error('bindAll must be passed function names');
+ for (i = 1; i < length; i++) {
+ key = arguments[i];
+ obj[key] = _.bind(obj[key], obj);
}
+ return obj;
+ };
- });
+ // Memoize an expensive function by storing its results.
+ _.memoize = function(func, hasher) {
+ var memoize = function(key) {
+ var cache = memoize.cache;
+ var address = '' + (hasher ? hasher.apply(this, arguments) : key);
+ if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
+ return cache[address];
+ };
+ memoize.cache = {};
+ return memoize;
+ };
- // Create the default Backbone.history.
- Backbone.history = new History;
+ // Delays a function for the given number of milliseconds, and then calls
+ // it with the arguments supplied.
+ _.delay = function(func, wait) {
+ var args = slice.call(arguments, 2);
+ return setTimeout(function(){
+ return func.apply(null, args);
+ }, wait);
+ };
- // Helpers
- // -------
+ // Defers a function, scheduling it to run after the current call stack has
+ // cleared.
+ _.defer = _.partial(_.delay, _, 1);
- // Helper function to correctly set up the prototype chain for subclasses.
- // Similar to `goog.inherits`, but uses a hash of prototype properties and
- // class properties to be extended.
- var extend = function(protoProps, staticProps) {
- var parent = this;
- var child;
+ // Returns a function, that, when invoked, will only be triggered at most once
+ // during a given window of time. Normally, the throttled function will run
+ // as much as it can, without ever going more than once per `wait` duration;
+ // but if you'd like to disable the execution on the leading edge, pass
+ // `{leading: false}`. To disable execution on the trailing edge, ditto.
+ _.throttle = function(func, wait, options) {
+ var context, args, result;
+ var timeout = null;
+ var previous = 0;
+ if (!options) options = {};
+ var later = function() {
+ previous = options.leading === false ? 0 : _.now();
+ timeout = null;
+ result = func.apply(context, args);
+ if (!timeout) context = args = null;
+ };
+ return function() {
+ var now = _.now();
+ if (!previous && options.leading === false) previous = now;
+ var remaining = wait - (now - previous);
+ context = this;
+ args = arguments;
+ if (remaining <= 0 || remaining > wait) {
+ if (timeout) {
+ clearTimeout(timeout);
+ timeout = null;
+ }
+ previous = now;
+ result = func.apply(context, args);
+ if (!timeout) context = args = null;
+ } else if (!timeout && options.trailing !== false) {
+ timeout = setTimeout(later, remaining);
+ }
+ return result;
+ };
+ };
- // The constructor function for the new subclass is either defined by you
- // (the "constructor" property in your `extend` definition), or defaulted
- // by us to simply call the parent constructor.
- if (protoProps && _.has(protoProps, 'constructor')) {
- child = protoProps.constructor;
- } else {
- child = function(){ return parent.apply(this, arguments); };
- }
+ // Returns a function, that, as long as it continues to be invoked, will not
+ // be triggered. The function will be called after it stops being called for
+ // N milliseconds. If `immediate` is passed, trigger the function on the
+ // leading edge, instead of the trailing.
+ _.debounce = function(func, wait, immediate) {
+ var timeout, args, context, timestamp, result;
- // Add static properties to the constructor function, if supplied.
- _.extend(child, parent, staticProps);
+ var later = function() {
+ var last = _.now() - timestamp;
- // Set the prototype chain to inherit from `parent`, without calling
- // `parent`'s constructor function and add the prototype properties.
- child.prototype = _.create(parent.prototype, protoProps);
- child.prototype.constructor = child;
+ if (last < wait && last >= 0) {
+ timeout = setTimeout(later, wait - last);
+ } else {
+ timeout = null;
+ if (!immediate) {
+ result = func.apply(context, args);
+ if (!timeout) context = args = null;
+ }
+ }
+ };
- // Set a convenience property in case the parent's prototype is needed
- // later.
- child.__super__ = parent.prototype;
+ return function() {
+ context = this;
+ args = arguments;
+ timestamp = _.now();
+ var callNow = immediate && !timeout;
+ if (!timeout) timeout = setTimeout(later, wait);
+ if (callNow) {
+ result = func.apply(context, args);
+ context = args = null;
+ }
- return child;
+ return result;
+ };
};
- // Set up inheritance for the model, collection, router, view and history.
- Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
-
- // Throw an error when a URL is needed, and none is supplied.
- var urlError = function() {
- throw new Error('A "url" property or function must be specified');
+ // Returns the first function passed as an argument to the second,
+ // allowing you to adjust arguments, run code before and after, and
+ // conditionally execute the original function.
+ _.wrap = function(func, wrapper) {
+ return _.partial(wrapper, func);
};
- // Wrap an optional error callback with a fallback error event.
- var wrapError = function(model, options) {
- var error = options.error;
- options.error = function(resp) {
- if (error) error.call(options.context, model, resp, options);
- model.trigger('error', model, resp, options);
+ // Returns a negated version of the passed-in predicate.
+ _.negate = function(predicate) {
+ return function() {
+ return !predicate.apply(this, arguments);
};
};
- return Backbone;
-});
+ // Returns a function that is the composition of a list of functions, each
+ // consuming the return value of the function that follows.
+ _.compose = function() {
+ var args = arguments;
+ var start = args.length - 1;
+ return function() {
+ var i = start;
+ var result = args[start].apply(this, arguments);
+ while (i--) result = args[i].call(this, result);
+ return result;
+ };
+ };
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7)))
+ // Returns a function that will only be executed on and after the Nth call.
+ _.after = function(times, func) {
+ return function() {
+ if (--times < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ };
-/***/ }),
-/* 1 */
-/***/ (function(module, exports, __webpack_require__) {
+ // Returns a function that will only be executed up to (but not including) the Nth call.
+ _.before = function(times, func) {
+ var memo;
+ return function() {
+ if (--times > 0) {
+ memo = func.apply(this, arguments);
+ }
+ if (times <= 1) func = null;
+ return memo;
+ };
+ };
-/* WEBPACK VAR INJECTION */(function(global, module) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Underscore.js 1.9.0
-// http://underscorejs.org
-// (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
-// Underscore may be freely distributed under the MIT license.
+ // Returns a function that will be executed at most one time, no matter how
+ // often you call it. Useful for lazy initialization.
+ _.once = _.partial(_.before, 2);
-(function() {
+ // Object Functions
+ // ----------------
- // Baseline setup
- // --------------
+ // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
+ var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
+ var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
+ 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
- // Establish the root object, `window` (`self`) in the browser, `global`
- // on the server, or `this` in some virtual machines. We use `self`
- // instead of `window` for `WebWorker` support.
- var root = typeof self == 'object' && self.self === self && self ||
- typeof global == 'object' && global.global === global && global ||
- this ||
- {};
+ function collectNonEnumProps(obj, keys) {
+ var nonEnumIdx = nonEnumerableProps.length;
+ var constructor = obj.constructor;
+ var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
- // Save the previous value of the `_` variable.
- var previousUnderscore = root._;
+ // Constructor is a special case.
+ var prop = 'constructor';
+ if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
- // Save bytes in the minified (but not gzipped) version:
- var ArrayProto = Array.prototype, ObjProto = Object.prototype;
- var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
+ while (nonEnumIdx--) {
+ prop = nonEnumerableProps[nonEnumIdx];
+ if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
+ keys.push(prop);
+ }
+ }
+ }
- // Create quick reference variables for speed access to core prototypes.
- var push = ArrayProto.push,
- slice = ArrayProto.slice,
- toString = ObjProto.toString,
- hasOwnProperty = ObjProto.hasOwnProperty;
+ // Retrieve the names of an object's own properties.
+ // Delegates to **ECMAScript 5**'s native `Object.keys`
+ _.keys = function(obj) {
+ if (!_.isObject(obj)) return [];
+ if (nativeKeys) return nativeKeys(obj);
+ var keys = [];
+ for (var key in obj) if (_.has(obj, key)) keys.push(key);
+ // Ahem, IE < 9.
+ if (hasEnumBug) collectNonEnumProps(obj, keys);
+ return keys;
+ };
- // All **ECMAScript 5** native function implementations that we hope to use
- // are declared here.
- var nativeIsArray = Array.isArray,
- nativeKeys = Object.keys,
- nativeCreate = Object.create;
+ // Retrieve all the property names of an object.
+ _.allKeys = function(obj) {
+ if (!_.isObject(obj)) return [];
+ var keys = [];
+ for (var key in obj) keys.push(key);
+ // Ahem, IE < 9.
+ if (hasEnumBug) collectNonEnumProps(obj, keys);
+ return keys;
+ };
- // Naked function reference for surrogate-prototype-swapping.
- var Ctor = function(){};
+ // Retrieve the values of an object's properties.
+ _.values = function(obj) {
+ var keys = _.keys(obj);
+ var length = keys.length;
+ var values = Array(length);
+ for (var i = 0; i < length; i++) {
+ values[i] = obj[keys[i]];
+ }
+ return values;
+ };
- // Create a safe reference to the Underscore object for use below.
- var _ = function(obj) {
- if (obj instanceof _) return obj;
- if (!(this instanceof _)) return new _(obj);
- this._wrapped = obj;
+ // Returns the results of applying the iteratee to each element of the object
+ // In contrast to _.map it returns an object
+ _.mapObject = function(obj, iteratee, context) {
+ iteratee = cb(iteratee, context);
+ var keys = _.keys(obj),
+ length = keys.length,
+ results = {},
+ currentKey;
+ for (var index = 0; index < length; index++) {
+ currentKey = keys[index];
+ results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
+ }
+ return results;
};
- // Export the Underscore object for **Node.js**, with
- // backwards-compatibility for their old module API. If we're in
- // the browser, add `_` as a global object.
- // (`nodeType` is checked to ensure that `module`
- // and `exports` are not HTML elements.)
- if (typeof exports != 'undefined' && !exports.nodeType) {
- if (typeof module != 'undefined' && !module.nodeType && module.exports) {
- exports = module.exports = _;
+ // Convert an object into a list of `[key, value]` pairs.
+ _.pairs = function(obj) {
+ var keys = _.keys(obj);
+ var length = keys.length;
+ var pairs = Array(length);
+ for (var i = 0; i < length; i++) {
+ pairs[i] = [keys[i], obj[keys[i]]];
}
- exports._ = _;
- } else {
- root._ = _;
- }
+ return pairs;
+ };
- // Current version.
- _.VERSION = '1.9.0';
+ // Invert the keys and values of an object. The values must be serializable.
+ _.invert = function(obj) {
+ var result = {};
+ var keys = _.keys(obj);
+ for (var i = 0, length = keys.length; i < length; i++) {
+ result[obj[keys[i]]] = keys[i];
+ }
+ return result;
+ };
- // Internal function that returns an efficient (for current engines) version
- // of the passed-in callback, to be repeatedly applied in other Underscore
- // functions.
- var optimizeCb = function(func, context, argCount) {
- if (context === void 0) return func;
- switch (argCount == null ? 3 : argCount) {
- case 1: return function(value) {
- return func.call(context, value);
- };
- // The 2-argument case is omitted because we’re not using it.
- case 3: return function(value, index, collection) {
- return func.call(context, value, index, collection);
- };
- case 4: return function(accumulator, value, index, collection) {
- return func.call(context, accumulator, value, index, collection);
- };
+ // Return a sorted list of the function names available on the object.
+ // Aliased as `methods`
+ _.functions = _.methods = function(obj) {
+ var names = [];
+ for (var key in obj) {
+ if (_.isFunction(obj[key])) names.push(key);
}
- return function() {
- return func.apply(context, arguments);
- };
+ return names.sort();
};
- var builtinIteratee;
+ // Extend a given object with all the properties in passed-in object(s).
+ _.extend = createAssigner(_.allKeys);
- // An internal function to generate callbacks that can be applied to each
- // element in a collection, returning the desired result — either `identity`,
- // an arbitrary callback, a property matcher, or a property accessor.
- var cb = function(value, context, argCount) {
- if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
- if (value == null) return _.identity;
- if (_.isFunction(value)) return optimizeCb(value, context, argCount);
- if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
- return _.property(value);
+ // Assigns a given object with all the own properties in the passed-in object(s)
+ // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
+ _.extendOwn = _.assign = createAssigner(_.keys);
+
+ // Returns the first key on an object that passes a predicate test
+ _.findKey = function(obj, predicate, context) {
+ predicate = cb(predicate, context);
+ var keys = _.keys(obj), key;
+ for (var i = 0, length = keys.length; i < length; i++) {
+ key = keys[i];
+ if (predicate(obj[key], key, obj)) return key;
+ }
};
- // External wrapper for our callback generator. Users may customize
- // `_.iteratee` if they want additional predicate/iteratee shorthand styles.
- // This abstraction hides the internal-only argCount argument.
- _.iteratee = builtinIteratee = function(value, context) {
- return cb(value, context, Infinity);
+ // Return a copy of the object only containing the whitelisted properties.
+ _.pick = function(object, oiteratee, context) {
+ var result = {}, obj = object, iteratee, keys;
+ if (obj == null) return result;
+ if (_.isFunction(oiteratee)) {
+ keys = _.allKeys(obj);
+ iteratee = optimizeCb(oiteratee, context);
+ } else {
+ keys = flatten(arguments, false, false, 1);
+ iteratee = function(value, key, obj) { return key in obj; };
+ obj = Object(obj);
+ }
+ for (var i = 0, length = keys.length; i < length; i++) {
+ var key = keys[i];
+ var value = obj[key];
+ if (iteratee(value, key, obj)) result[key] = value;
+ }
+ return result;
};
- // Some functions take a variable number of arguments, or a few expected
- // arguments at the beginning and then a variable number of values to operate
- // on. This helper accumulates all remaining arguments past the function’s
- // argument length (or an explicit `startIndex`), into an array that becomes
- // the last argument. Similar to ES6’s "rest parameter".
- var restArguments = function(func, startIndex) {
- startIndex = startIndex == null ? func.length - 1 : +startIndex;
- return function() {
- var length = Math.max(arguments.length - startIndex, 0),
- rest = Array(length),
- index = 0;
- for (; index < length; index++) {
- rest[index] = arguments[index + startIndex];
- }
- switch (startIndex) {
- case 0: return func.call(this, rest);
- case 1: return func.call(this, arguments[0], rest);
- case 2: return func.call(this, arguments[0], arguments[1], rest);
- }
- var args = Array(startIndex + 1);
- for (index = 0; index < startIndex; index++) {
- args[index] = arguments[index];
- }
- args[startIndex] = rest;
- return func.apply(this, args);
- };
+ // Return a copy of the object without the blacklisted properties.
+ _.omit = function(obj, iteratee, context) {
+ if (_.isFunction(iteratee)) {
+ iteratee = _.negate(iteratee);
+ } else {
+ var keys = _.map(flatten(arguments, false, false, 1), String);
+ iteratee = function(value, key) {
+ return !_.contains(keys, key);
+ };
+ }
+ return _.pick(obj, iteratee, context);
};
- // An internal function for creating a new object that inherits from another.
- var baseCreate = function(prototype) {
- if (!_.isObject(prototype)) return {};
- if (nativeCreate) return nativeCreate(prototype);
- Ctor.prototype = prototype;
- var result = new Ctor;
- Ctor.prototype = null;
+ // Fill in a given object with default properties.
+ _.defaults = createAssigner(_.allKeys, true);
+
+ // Creates an object that inherits from the given prototype object.
+ // If additional properties are provided then they will be added to the
+ // created object.
+ _.create = function(prototype, props) {
+ var result = baseCreate(prototype);
+ if (props) _.extendOwn(result, props);
return result;
};
- var shallowProperty = function(key) {
- return function(obj) {
- return obj == null ? void 0 : obj[key];
- };
+ // Create a (shallow-cloned) duplicate of an object.
+ _.clone = function(obj) {
+ if (!_.isObject(obj)) return obj;
+ return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
- var deepGet = function(obj, path) {
- var length = path.length;
- for (var i = 0; i < length; i++) {
- if (obj == null) return void 0;
- obj = obj[path[i]];
- }
- return length ? obj : void 0;
+ // Invokes interceptor with the obj, and then returns obj.
+ // The primary purpose of this method is to "tap into" a method chain, in
+ // order to perform operations on intermediate results within the chain.
+ _.tap = function(obj, interceptor) {
+ interceptor(obj);
+ return obj;
};
- // Helper for collection methods to determine whether a collection
- // should be iterated as an array or as an object.
- // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
- // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
- var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
- var getLength = shallowProperty('length');
- var isArrayLike = function(collection) {
- var length = getLength(collection);
- return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
+ // Returns whether an object has a given set of `key:value` pairs.
+ _.isMatch = function(object, attrs) {
+ var keys = _.keys(attrs), length = keys.length;
+ if (object == null) return !length;
+ var obj = Object(object);
+ for (var i = 0; i < length; i++) {
+ var key = keys[i];
+ if (attrs[key] !== obj[key] || !(key in obj)) return false;
+ }
+ return true;
};
- // Collection Functions
- // --------------------
- // The cornerstone, an `each` implementation, aka `forEach`.
- // Handles raw objects in addition to array-likes. Treats all
- // sparse array-likes as if they were dense.
- _.each = _.forEach = function(obj, iteratee, context) {
- iteratee = optimizeCb(iteratee, context);
- var i, length;
- if (isArrayLike(obj)) {
- for (i = 0, length = obj.length; i < length; i++) {
- iteratee(obj[i], i, obj);
- }
- } else {
- var keys = _.keys(obj);
- for (i = 0, length = keys.length; i < length; i++) {
- iteratee(obj[keys[i]], keys[i], obj);
- }
+ // Internal recursive comparison function for `isEqual`.
+ var eq = function(a, b, aStack, bStack) {
+ // Identical objects are equal. `0 === -0`, but they aren't identical.
+ // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
+ if (a === b) return a !== 0 || 1 / a === 1 / b;
+ // A strict comparison is necessary because `null == undefined`.
+ if (a == null || b == null) return a === b;
+ // Unwrap any wrapped objects.
+ if (a instanceof _) a = a._wrapped;
+ if (b instanceof _) b = b._wrapped;
+ // Compare `[[Class]]` names.
+ var className = toString.call(a);
+ if (className !== toString.call(b)) return false;
+ switch (className) {
+ // Strings, numbers, regular expressions, dates, and booleans are compared by value.
+ case '[object RegExp]':
+ // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
+ case '[object String]':
+ // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+ // equivalent to `new String("5")`.
+ return '' + a === '' + b;
+ case '[object Number]':
+ // `NaN`s are equivalent, but non-reflexive.
+ // Object(NaN) is equivalent to NaN
+ if (+a !== +a) return +b !== +b;
+ // An `egal` comparison is performed for other numeric values.
+ return +a === 0 ? 1 / +a === 1 / b : +a === +b;
+ case '[object Date]':
+ case '[object Boolean]':
+ // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+ // millisecond representations. Note that invalid dates with millisecond representations
+ // of `NaN` are not equivalent.
+ return +a === +b;
}
- return obj;
- };
- // Return the results of applying the iteratee to each element.
- _.map = _.collect = function(obj, iteratee, context) {
- iteratee = cb(iteratee, context);
- var keys = !isArrayLike(obj) && _.keys(obj),
- length = (keys || obj).length,
- results = Array(length);
- for (var index = 0; index < length; index++) {
- var currentKey = keys ? keys[index] : index;
- results[index] = iteratee(obj[currentKey], currentKey, obj);
- }
- return results;
- };
+ var areArrays = className === '[object Array]';
+ if (!areArrays) {
+ if (typeof a != 'object' || typeof b != 'object') return false;
- // Create a reducing function iterating left or right.
- var createReduce = function(dir) {
- // Wrap code that reassigns argument variables in a separate function than
- // the one that accesses `arguments.length` to avoid a perf hit. (#1991)
- var reducer = function(obj, iteratee, memo, initial) {
- var keys = !isArrayLike(obj) && _.keys(obj),
- length = (keys || obj).length,
- index = dir > 0 ? 0 : length - 1;
- if (!initial) {
- memo = obj[keys ? keys[index] : index];
- index += dir;
- }
- for (; index >= 0 && index < length; index += dir) {
- var currentKey = keys ? keys[index] : index;
- memo = iteratee(memo, obj[currentKey], currentKey, obj);
+ // Objects with different constructors are not equivalent, but `Object`s or `Array`s
+ // from different frames are.
+ var aCtor = a.constructor, bCtor = b.constructor;
+ if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
+ _.isFunction(bCtor) && bCtor instanceof bCtor)
+ && ('constructor' in a && 'constructor' in b)) {
+ return false;
}
- return memo;
- };
-
- return function(obj, iteratee, memo, context) {
- var initial = arguments.length >= 3;
- return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
- };
- };
+ }
+ // Assume equality for cyclic structures. The algorithm for detecting cyclic
+ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
- // **Reduce** builds up a single result from a list of values, aka `inject`,
- // or `foldl`.
- _.reduce = _.foldl = _.inject = createReduce(1);
+ // Initializing stack of traversed objects.
+ // It's done here since we only need them for objects and arrays comparison.
+ aStack = aStack || [];
+ bStack = bStack || [];
+ var length = aStack.length;
+ while (length--) {
+ // Linear search. Performance is inversely proportional to the number of
+ // unique nested structures.
+ if (aStack[length] === a) return bStack[length] === b;
+ }
- // The right-associative version of reduce, also known as `foldr`.
- _.reduceRight = _.foldr = createReduce(-1);
+ // Add the first object to the stack of traversed objects.
+ aStack.push(a);
+ bStack.push(b);
- // Return the first value which passes a truth test. Aliased as `detect`.
- _.find = _.detect = function(obj, predicate, context) {
- var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey;
- var key = keyFinder(obj, predicate, context);
- if (key !== void 0 && key !== -1) return obj[key];
+ // Recursively compare objects and arrays.
+ if (areArrays) {
+ // Compare array lengths to determine if a deep comparison is necessary.
+ length = a.length;
+ if (length !== b.length) return false;
+ // Deep compare the contents, ignoring non-numeric properties.
+ while (length--) {
+ if (!eq(a[length], b[length], aStack, bStack)) return false;
+ }
+ } else {
+ // Deep compare objects.
+ var keys = _.keys(a), key;
+ length = keys.length;
+ // Ensure that both objects contain the same number of properties before comparing deep equality.
+ if (_.keys(b).length !== length) return false;
+ while (length--) {
+ // Deep compare each member
+ key = keys[length];
+ if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
+ }
+ }
+ // Remove the first object from the stack of traversed objects.
+ aStack.pop();
+ bStack.pop();
+ return true;
};
- // Return all the elements that pass a truth test.
- // Aliased as `select`.
- _.filter = _.select = function(obj, predicate, context) {
- var results = [];
- predicate = cb(predicate, context);
- _.each(obj, function(value, index, list) {
- if (predicate(value, index, list)) results.push(value);
- });
- return results;
+ // Perform a deep comparison to check if two objects are equal.
+ _.isEqual = function(a, b) {
+ return eq(a, b);
};
- // Return all the elements for which a truth test fails.
- _.reject = function(obj, predicate, context) {
- return _.filter(obj, _.negate(cb(predicate)), context);
+ // Is a given array, string, or object empty?
+ // An "empty" object has no enumerable own-properties.
+ _.isEmpty = function(obj) {
+ if (obj == null) return true;
+ if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
+ return _.keys(obj).length === 0;
};
- // Determine whether all of the elements match a truth test.
- // Aliased as `all`.
- _.every = _.all = function(obj, predicate, context) {
- predicate = cb(predicate, context);
- var keys = !isArrayLike(obj) && _.keys(obj),
- length = (keys || obj).length;
- for (var index = 0; index < length; index++) {
- var currentKey = keys ? keys[index] : index;
- if (!predicate(obj[currentKey], currentKey, obj)) return false;
- }
- return true;
+ // Is a given value a DOM element?
+ _.isElement = function(obj) {
+ return !!(obj && obj.nodeType === 1);
};
- // Determine if at least one element in the object matches a truth test.
- // Aliased as `any`.
- _.some = _.any = function(obj, predicate, context) {
- predicate = cb(predicate, context);
- var keys = !isArrayLike(obj) && _.keys(obj),
- length = (keys || obj).length;
- for (var index = 0; index < length; index++) {
- var currentKey = keys ? keys[index] : index;
- if (predicate(obj[currentKey], currentKey, obj)) return true;
- }
- return false;
+ // Is a given value an array?
+ // Delegates to ECMA5's native Array.isArray
+ _.isArray = nativeIsArray || function(obj) {
+ return toString.call(obj) === '[object Array]';
};
- // Determine if the array or object contains a given item (using `===`).
- // Aliased as `includes` and `include`.
- _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
- if (!isArrayLike(obj)) obj = _.values(obj);
- if (typeof fromIndex != 'number' || guard) fromIndex = 0;
- return _.indexOf(obj, item, fromIndex) >= 0;
+ // Is a given variable an object?
+ _.isObject = function(obj) {
+ var type = typeof obj;
+ return type === 'function' || type === 'object' && !!obj;
};
- // Invoke a method (with arguments) on every item in a collection.
- _.invoke = restArguments(function(obj, path, args) {
- var contextPath, func;
- if (_.isFunction(path)) {
- func = path;
- } else if (_.isArray(path)) {
- contextPath = path.slice(0, -1);
- path = path[path.length - 1];
- }
- return _.map(obj, function(context) {
- var method = func;
- if (!method) {
- if (contextPath && contextPath.length) {
- context = deepGet(context, contextPath);
- }
- if (context == null) return void 0;
- method = context[path];
- }
- return method == null ? method : method.apply(context, args);
- });
+ // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
+ _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
+ _['is' + name] = function(obj) {
+ return toString.call(obj) === '[object ' + name + ']';
+ };
});
- // Convenience version of a common use case of `map`: fetching a property.
- _.pluck = function(obj, key) {
- return _.map(obj, _.property(key));
- };
-
- // Convenience version of a common use case of `filter`: selecting only objects
- // containing specific `key:value` pairs.
- _.where = function(obj, attrs) {
- return _.filter(obj, _.matcher(attrs));
- };
+ // Define a fallback version of the method in browsers (ahem, IE < 9), where
+ // there isn't any inspectable "Arguments" type.
+ if (!_.isArguments(arguments)) {
+ _.isArguments = function(obj) {
+ return _.has(obj, 'callee');
+ };
+ }
- // Convenience version of a common use case of `find`: getting the first object
- // containing specific `key:value` pairs.
- _.findWhere = function(obj, attrs) {
- return _.find(obj, _.matcher(attrs));
- };
+ // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
+ // IE 11 (#1621), and in Safari 8 (#1929).
+ if (typeof /./ != 'function' && typeof Int8Array != 'object') {
+ _.isFunction = function(obj) {
+ return typeof obj == 'function' || false;
+ };
+ }
- // Return the maximum element (or element-based computation).
- _.max = function(obj, iteratee, context) {
- var result = -Infinity, lastComputed = -Infinity,
- value, computed;
- if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
- obj = isArrayLike(obj) ? obj : _.values(obj);
- for (var i = 0, length = obj.length; i < length; i++) {
- value = obj[i];
- if (value != null && value > result) {
- result = value;
- }
- }
- } else {
- iteratee = cb(iteratee, context);
- _.each(obj, function(v, index, list) {
- computed = iteratee(v, index, list);
- if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
- result = v;
- lastComputed = computed;
- }
- });
- }
- return result;
+ // Is a given object a finite number?
+ _.isFinite = function(obj) {
+ return isFinite(obj) && !isNaN(parseFloat(obj));
};
- // Return the minimum element (or element-based computation).
- _.min = function(obj, iteratee, context) {
- var result = Infinity, lastComputed = Infinity,
- value, computed;
- if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
- obj = isArrayLike(obj) ? obj : _.values(obj);
- for (var i = 0, length = obj.length; i < length; i++) {
- value = obj[i];
- if (value != null && value < result) {
- result = value;
- }
- }
- } else {
- iteratee = cb(iteratee, context);
- _.each(obj, function(v, index, list) {
- computed = iteratee(v, index, list);
- if (computed < lastComputed || computed === Infinity && result === Infinity) {
- result = v;
- lastComputed = computed;
- }
- });
- }
- return result;
+ // Is the given value `NaN`? (NaN is the only number which does not equal itself).
+ _.isNaN = function(obj) {
+ return _.isNumber(obj) && obj !== +obj;
};
- // Shuffle a collection.
- _.shuffle = function(obj) {
- return _.sample(obj, Infinity);
+ // Is a given value a boolean?
+ _.isBoolean = function(obj) {
+ return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
};
- // Sample **n** random values from a collection using the modern version of the
- // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
- // If **n** is not specified, returns a single random element.
- // The internal `guard` argument allows it to work with `map`.
- _.sample = function(obj, n, guard) {
- if (n == null || guard) {
- if (!isArrayLike(obj)) obj = _.values(obj);
- return obj[_.random(obj.length - 1)];
- }
- var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
- var length = getLength(sample);
- n = Math.max(Math.min(n, length), 0);
- var last = length - 1;
- for (var index = 0; index < n; index++) {
- var rand = _.random(index, last);
- var temp = sample[index];
- sample[index] = sample[rand];
- sample[rand] = temp;
- }
- return sample.slice(0, n);
+ // Is a given value equal to null?
+ _.isNull = function(obj) {
+ return obj === null;
};
- // Sort the object's values by a criterion produced by an iteratee.
- _.sortBy = function(obj, iteratee, context) {
- var index = 0;
- iteratee = cb(iteratee, context);
- return _.pluck(_.map(obj, function(value, key, list) {
- return {
- value: value,
- index: index++,
- criteria: iteratee(value, key, list)
- };
- }).sort(function(left, right) {
- var a = left.criteria;
- var b = right.criteria;
- if (a !== b) {
- if (a > b || a === void 0) return 1;
- if (a < b || b === void 0) return -1;
- }
- return left.index - right.index;
- }), 'value');
+ // Is a given variable undefined?
+ _.isUndefined = function(obj) {
+ return obj === void 0;
};
- // An internal function used for aggregate "group by" operations.
- var group = function(behavior, partition) {
- return function(obj, iteratee, context) {
- var result = partition ? [[], []] : {};
- iteratee = cb(iteratee, context);
- _.each(obj, function(value, index) {
- var key = iteratee(value, index, obj);
- behavior(result, value, key);
- });
- return result;
- };
+ // Shortcut function for checking if an object has a given property directly
+ // on itself (in other words, not on a prototype).
+ _.has = function(obj, key) {
+ return obj != null && hasOwnProperty.call(obj, key);
};
- // Groups the object's values by a criterion. Pass either a string attribute
- // to group by, or a function that returns the criterion.
- _.groupBy = group(function(result, value, key) {
- if (_.has(result, key)) result[key].push(value); else result[key] = [value];
- });
-
- // Indexes the object's values by a criterion, similar to `groupBy`, but for
- // when you know that your index values will be unique.
- _.indexBy = group(function(result, value, key) {
- result[key] = value;
- });
+ // Utility Functions
+ // -----------------
- // Counts instances of an object that group by a certain criterion. Pass
- // either a string attribute to count by, or a function that returns the
- // criterion.
- _.countBy = group(function(result, value, key) {
- if (_.has(result, key)) result[key]++; else result[key] = 1;
- });
+ // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
+ // previous owner. Returns a reference to the Underscore object.
+ _.noConflict = function() {
+ root._ = previousUnderscore;
+ return this;
+ };
- var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
- // Safely create a real, live array from anything iterable.
- _.toArray = function(obj) {
- if (!obj) return [];
- if (_.isArray(obj)) return slice.call(obj);
- if (_.isString(obj)) {
- // Keep surrogate pair characters together
- return obj.match(reStrSymbol);
- }
- if (isArrayLike(obj)) return _.map(obj, _.identity);
- return _.values(obj);
+ // Keep the identity function around for default iteratees.
+ _.identity = function(value) {
+ return value;
};
- // Return the number of elements in an object.
- _.size = function(obj) {
- if (obj == null) return 0;
- return isArrayLike(obj) ? obj.length : _.keys(obj).length;
+ // Predicate-generating functions. Often useful outside of Underscore.
+ _.constant = function(value) {
+ return function() {
+ return value;
+ };
};
- // Split a collection into two arrays: one whose elements all satisfy the given
- // predicate, and one whose elements all do not satisfy the predicate.
- _.partition = group(function(result, value, pass) {
- result[pass ? 0 : 1].push(value);
- }, true);
+ _.noop = function(){};
- // Array Functions
- // ---------------
+ _.property = property;
- // Get the first element of an array. Passing **n** will return the first N
- // values in the array. Aliased as `head` and `take`. The **guard** check
- // allows it to work with `_.map`.
- _.first = _.head = _.take = function(array, n, guard) {
- if (array == null || array.length < 1) return void 0;
- if (n == null || guard) return array[0];
- return _.initial(array, array.length - n);
+ // Generates a function for a given object that returns a given property.
+ _.propertyOf = function(obj) {
+ return obj == null ? function(){} : function(key) {
+ return obj[key];
+ };
};
- // Returns everything but the last entry of the array. Especially useful on
- // the arguments object. Passing **n** will return all the values in
- // the array, excluding the last N.
- _.initial = function(array, n, guard) {
- return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
+ // Returns a predicate for checking whether an object has a given set of
+ // `key:value` pairs.
+ _.matcher = _.matches = function(attrs) {
+ attrs = _.extendOwn({}, attrs);
+ return function(obj) {
+ return _.isMatch(obj, attrs);
+ };
};
- // Get the last element of an array. Passing **n** will return the last N
- // values in the array.
- _.last = function(array, n, guard) {
- if (array == null || array.length < 1) return void 0;
- if (n == null || guard) return array[array.length - 1];
- return _.rest(array, Math.max(0, array.length - n));
+ // Run a function **n** times.
+ _.times = function(n, iteratee, context) {
+ var accum = Array(Math.max(0, n));
+ iteratee = optimizeCb(iteratee, context, 1);
+ for (var i = 0; i < n; i++) accum[i] = iteratee(i);
+ return accum;
};
- // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
- // Especially useful on the arguments object. Passing an **n** will return
- // the rest N values in the array.
- _.rest = _.tail = _.drop = function(array, n, guard) {
- return slice.call(array, n == null || guard ? 1 : n);
+ // Return a random integer between min and max (inclusive).
+ _.random = function(min, max) {
+ if (max == null) {
+ max = min;
+ min = 0;
+ }
+ return min + Math.floor(Math.random() * (max - min + 1));
};
- // Trim out all falsy values from an array.
- _.compact = function(array) {
- return _.filter(array, Boolean);
+ // A (possibly faster) way to get the current timestamp as an integer.
+ _.now = Date.now || function() {
+ return new Date().getTime();
};
- // Internal implementation of a recursive `flatten` function.
- var flatten = function(input, shallow, strict, output) {
- output = output || [];
- var idx = output.length;
- for (var i = 0, length = getLength(input); i < length; i++) {
- var value = input[i];
- if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
- // Flatten current level of array or arguments object.
- if (shallow) {
- var j = 0, len = value.length;
- while (j < len) output[idx++] = value[j++];
- } else {
- flatten(value, shallow, strict, output);
- idx = output.length;
- }
- } else if (!strict) {
- output[idx++] = value;
- }
- }
- return output;
+ // List of HTML entities for escaping.
+ var escapeMap = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ '`': '`'
};
+ var unescapeMap = _.invert(escapeMap);
- // Flatten out an array, either recursively (by default), or just one level.
- _.flatten = function(array, shallow) {
- return flatten(array, shallow, false);
+ // Functions for escaping and unescaping strings to/from HTML interpolation.
+ var createEscaper = function(map) {
+ var escaper = function(match) {
+ return map[match];
+ };
+ // Regexes for identifying a key that needs to be escaped
+ var source = '(?:' + _.keys(map).join('|') + ')';
+ var testRegexp = RegExp(source);
+ var replaceRegexp = RegExp(source, 'g');
+ return function(string) {
+ string = string == null ? '' : '' + string;
+ return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
+ };
};
+ _.escape = createEscaper(escapeMap);
+ _.unescape = createEscaper(unescapeMap);
- // Return a version of the array that does not contain the specified value(s).
- _.without = restArguments(function(array, otherArrays) {
- return _.difference(array, otherArrays);
- });
-
- // Produce a duplicate-free version of the array. If the array has already
- // been sorted, you have the option of using a faster algorithm.
- // The faster algorithm will not work with an iteratee if the iteratee
- // is not a one-to-one function, so providing an iteratee will disable
- // the faster algorithm.
- // Aliased as `unique`.
- _.uniq = _.unique = function(array, isSorted, iteratee, context) {
- if (!_.isBoolean(isSorted)) {
- context = iteratee;
- iteratee = isSorted;
- isSorted = false;
- }
- if (iteratee != null) iteratee = cb(iteratee, context);
- var result = [];
- var seen = [];
- for (var i = 0, length = getLength(array); i < length; i++) {
- var value = array[i],
- computed = iteratee ? iteratee(value, i, array) : value;
- if (isSorted && !iteratee) {
- if (!i || seen !== computed) result.push(value);
- seen = computed;
- } else if (iteratee) {
- if (!_.contains(seen, computed)) {
- seen.push(computed);
- result.push(value);
- }
- } else if (!_.contains(result, value)) {
- result.push(value);
- }
+ // If the value of the named `property` is a function then invoke it with the
+ // `object` as context; otherwise, return it.
+ _.result = function(object, property, fallback) {
+ var value = object == null ? void 0 : object[property];
+ if (value === void 0) {
+ value = fallback;
}
- return result;
+ return _.isFunction(value) ? value.call(object) : value;
};
- // Produce an array that contains the union: each distinct element from all of
- // the passed-in arrays.
- _.union = restArguments(function(arrays) {
- return _.uniq(flatten(arrays, true, true));
- });
-
- // Produce an array that contains every item shared between all the
- // passed-in arrays.
- _.intersection = function(array) {
- var result = [];
- var argsLength = arguments.length;
- for (var i = 0, length = getLength(array); i < length; i++) {
- var item = array[i];
- if (_.contains(result, item)) continue;
- var j;
- for (j = 1; j < argsLength; j++) {
- if (!_.contains(arguments[j], item)) break;
- }
- if (j === argsLength) result.push(item);
- }
- return result;
+ // Generate a unique integer id (unique within the entire client session).
+ // Useful for temporary DOM ids.
+ var idCounter = 0;
+ _.uniqueId = function(prefix) {
+ var id = ++idCounter + '';
+ return prefix ? prefix + id : id;
};
- // Take the difference between one array and a number of other arrays.
- // Only the elements present in just the first array will remain.
- _.difference = restArguments(function(array, rest) {
- rest = flatten(rest, true, true);
- return _.filter(array, function(value){
- return !_.contains(rest, value);
- });
- });
-
- // Complement of _.zip. Unzip accepts an array of arrays and groups
- // each array's elements on shared indices.
- _.unzip = function(array) {
- var length = array && _.max(array, getLength).length || 0;
- var result = Array(length);
-
- for (var index = 0; index < length; index++) {
- result[index] = _.pluck(array, index);
- }
- return result;
+ // By default, Underscore uses ERB-style template delimiters, change the
+ // following template settings to use alternative delimiters.
+ _.templateSettings = {
+ evaluate : /<%([\s\S]+?)%>/g,
+ interpolate : /<%=([\s\S]+?)%>/g,
+ escape : /<%-([\s\S]+?)%>/g
};
- // Zip together multiple lists into a single array -- elements that share
- // an index go together.
- _.zip = restArguments(_.unzip);
+ // When customizing `templateSettings`, if you don't want to define an
+ // interpolation, evaluation or escaping regex, we need one that is
+ // guaranteed not to match.
+ var noMatch = /(.)^/;
- // Converts lists into objects. Pass either a single array of `[key, value]`
- // pairs, or two parallel arrays of the same length -- one of keys, and one of
- // the corresponding values. Passing by pairs is the reverse of _.pairs.
- _.object = function(list, values) {
- var result = {};
- for (var i = 0, length = getLength(list); i < length; i++) {
- if (values) {
- result[list[i]] = values[i];
- } else {
- result[list[i][0]] = list[i][1];
- }
- }
- return result;
+ // Certain characters need to be escaped so that they can be put into a
+ // string literal.
+ var escapes = {
+ "'": "'",
+ '\\': '\\',
+ '\r': 'r',
+ '\n': 'n',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
};
- // Generator function to create the findIndex and findLastIndex functions.
- var createPredicateIndexFinder = function(dir) {
- return function(array, predicate, context) {
- predicate = cb(predicate, context);
- var length = getLength(array);
- var index = dir > 0 ? 0 : length - 1;
- for (; index >= 0 && index < length; index += dir) {
- if (predicate(array[index], index, array)) return index;
- }
- return -1;
- };
+ var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
+
+ var escapeChar = function(match) {
+ return '\\' + escapes[match];
};
- // Returns the first index on an array-like that passes a predicate test.
- _.findIndex = createPredicateIndexFinder(1);
- _.findLastIndex = createPredicateIndexFinder(-1);
+ // JavaScript micro-templating, similar to John Resig's implementation.
+ // Underscore templating handles arbitrary delimiters, preserves whitespace,
+ // and correctly escapes quotes within interpolated code.
+ // NB: `oldSettings` only exists for backwards compatibility.
+ _.template = function(text, settings, oldSettings) {
+ if (!settings && oldSettings) settings = oldSettings;
+ settings = _.defaults({}, settings, _.templateSettings);
- // Use a comparator function to figure out the smallest index at which
- // an object should be inserted so as to maintain order. Uses binary search.
- _.sortedIndex = function(array, obj, iteratee, context) {
- iteratee = cb(iteratee, context, 1);
- var value = iteratee(obj);
- var low = 0, high = getLength(array);
- while (low < high) {
- var mid = Math.floor((low + high) / 2);
- if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
- }
- return low;
- };
+ // Combine delimiters into one regular expression via alternation.
+ var matcher = RegExp([
+ (settings.escape || noMatch).source,
+ (settings.interpolate || noMatch).source,
+ (settings.evaluate || noMatch).source
+ ].join('|') + '|$', 'g');
- // Generator function to create the indexOf and lastIndexOf functions.
- var createIndexFinder = function(dir, predicateFind, sortedIndex) {
- return function(array, item, idx) {
- var i = 0, length = getLength(array);
- if (typeof idx == 'number') {
- if (dir > 0) {
- i = idx >= 0 ? idx : Math.max(idx + length, i);
- } else {
- length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
- }
- } else if (sortedIndex && idx && length) {
- idx = sortedIndex(array, item);
- return array[idx] === item ? idx : -1;
- }
- if (item !== item) {
- idx = predicateFind(slice.call(array, i, length), _.isNaN);
- return idx >= 0 ? idx + i : -1;
- }
- for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
- if (array[idx] === item) return idx;
+ // Compile the template source, escaping string literals appropriately.
+ var index = 0;
+ var source = "__p+='";
+ text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
+ source += text.slice(index, offset).replace(escaper, escapeChar);
+ index = offset + match.length;
+
+ if (escape) {
+ source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
+ } else if (interpolate) {
+ source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
+ } else if (evaluate) {
+ source += "';\n" + evaluate + "\n__p+='";
}
- return -1;
- };
- };
- // Return the position of the first occurrence of an item in an array,
- // or -1 if the item is not included in the array.
- // If the array is large and already in sort order, pass `true`
- // for **isSorted** to use binary search.
- _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
- _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
+ // Adobe VMs need the match returned to produce the correct offest.
+ return match;
+ });
+ source += "';\n";
- // Generate an integer Array containing an arithmetic progression. A port of
- // the native Python `range()` function. See
- // [the Python documentation](http://docs.python.org/library/functions.html#range).
- _.range = function(start, stop, step) {
- if (stop == null) {
- stop = start || 0;
- start = 0;
- }
- if (!step) {
- step = stop < start ? -1 : 1;
- }
+ // If a variable is not specified, place data values in local scope.
+ if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
- var length = Math.max(Math.ceil((stop - start) / step), 0);
- var range = Array(length);
+ source = "var __t,__p='',__j=Array.prototype.join," +
+ "print=function(){__p+=__j.call(arguments,'');};\n" +
+ source + 'return __p;\n';
- for (var idx = 0; idx < length; idx++, start += step) {
- range[idx] = start;
+ try {
+ var render = new Function(settings.variable || 'obj', '_', source);
+ } catch (e) {
+ e.source = source;
+ throw e;
}
- return range;
+ var template = function(data) {
+ return render.call(this, data, _);
+ };
+
+ // Provide the compiled source as a convenience for precompilation.
+ var argument = settings.variable || 'obj';
+ template.source = 'function(' + argument + '){\n' + source + '}';
+
+ return template;
};
- // Chunk a single array into multiple arrays, each containing `count` or fewer
- // items.
- _.chunk = function(array, count) {
- if (count == null || count < 1) return [];
- var result = [];
- var i = 0, length = array.length;
- while (i < length) {
- result.push(slice.call(array, i, i += count));
- }
- return result;
+ // Add a "chain" function. Start chaining a wrapped Underscore object.
+ _.chain = function(obj) {
+ var instance = _(obj);
+ instance._chain = true;
+ return instance;
};
- // Function (ahem) Functions
- // ------------------
+ // OOP
+ // ---------------
+ // If Underscore is called as a function, it returns a wrapped object that
+ // can be used OO-style. This wrapper holds altered versions of all the
+ // underscore functions. Wrapped objects may be chained.
- // Determines whether to execute a function as a constructor
- // or a normal function with the provided arguments.
- var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
- if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
- var self = baseCreate(sourceFunc.prototype);
- var result = sourceFunc.apply(self, args);
- if (_.isObject(result)) return result;
- return self;
+ // Helper function to continue chaining intermediate results.
+ var result = function(instance, obj) {
+ return instance._chain ? _(obj).chain() : obj;
};
- // Create a function bound to a given object (assigning `this`, and arguments,
- // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
- // available.
- _.bind = restArguments(function(func, context, args) {
- if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
- var bound = restArguments(function(callArgs) {
- return executeBound(func, bound, context, this, args.concat(callArgs));
+ // Add your own custom functions to the Underscore object.
+ _.mixin = function(obj) {
+ _.each(_.functions(obj), function(name) {
+ var func = _[name] = obj[name];
+ _.prototype[name] = function() {
+ var args = [this._wrapped];
+ push.apply(args, arguments);
+ return result(this, func.apply(_, args));
+ };
});
- return bound;
- });
+ };
- // Partially apply a function by creating a version that has had some of its
- // arguments pre-filled, without changing its dynamic `this` context. _ acts
- // as a placeholder by default, allowing any combination of arguments to be
- // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument.
- _.partial = restArguments(function(func, boundArgs) {
- var placeholder = _.partial.placeholder;
- var bound = function() {
- var position = 0, length = boundArgs.length;
- var args = Array(length);
- for (var i = 0; i < length; i++) {
- args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
- }
- while (position < arguments.length) args.push(arguments[position++]);
- return executeBound(func, bound, this, this, args);
+ // Add all of the Underscore functions to the wrapper object.
+ _.mixin(_);
+
+ // Add all mutator Array functions to the wrapper.
+ _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ var method = ArrayProto[name];
+ _.prototype[name] = function() {
+ var obj = this._wrapped;
+ method.apply(obj, arguments);
+ if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
+ return result(this, obj);
};
- return bound;
});
- _.partial.placeholder = _;
-
- // Bind a number of an object's methods to that object. Remaining arguments
- // are the method names to be bound. Useful for ensuring that all callbacks
- // defined on an object belong to it.
- _.bindAll = restArguments(function(obj, keys) {
- keys = flatten(keys, false, false);
- var index = keys.length;
- if (index < 1) throw new Error('bindAll must be passed function names');
- while (index--) {
- var key = keys[index];
- obj[key] = _.bind(obj[key], obj);
- }
+ // Add all accessor Array functions to the wrapper.
+ _.each(['concat', 'join', 'slice'], function(name) {
+ var method = ArrayProto[name];
+ _.prototype[name] = function() {
+ return result(this, method.apply(this._wrapped, arguments));
+ };
});
- // Memoize an expensive function by storing its results.
- _.memoize = function(func, hasher) {
- var memoize = function(key) {
- var cache = memoize.cache;
- var address = '' + (hasher ? hasher.apply(this, arguments) : key);
- if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
- return cache[address];
- };
- memoize.cache = {};
- return memoize;
+ // Extracts the result from a wrapped and chained object.
+ _.prototype.value = function() {
+ return this._wrapped;
};
- // Delays a function for the given number of milliseconds, and then calls
- // it with the arguments supplied.
- _.delay = restArguments(function(func, wait, args) {
- return setTimeout(function() {
- return func.apply(null, args);
- }, wait);
- });
+ // Provide unwrapping proxy for some methods used in engine operations
+ // such as arithmetic and JSON stringification.
+ _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
- // Defers a function, scheduling it to run after the current call stack has
- // cleared.
- _.defer = _.partial(_.delay, _, 1);
+ _.prototype.toString = function() {
+ return '' + this._wrapped;
+ };
- // Returns a function, that, when invoked, will only be triggered at most once
- // during a given window of time. Normally, the throttled function will run
- // as much as it can, without ever going more than once per `wait` duration;
- // but if you'd like to disable the execution on the leading edge, pass
- // `{leading: false}`. To disable execution on the trailing edge, ditto.
- _.throttle = function(func, wait, options) {
- var timeout, context, args, result;
- var previous = 0;
- if (!options) options = {};
+ // AMD registration happens at the end for compatibility with AMD loaders
+ // that may not enforce next-turn semantics on modules. Even though general
+ // practice for AMD registration is to be anonymous, underscore registers
+ // as a named module because, like jQuery, it is a base library that is
+ // popular enough to be bundled in a third party lib, but not be part of
+ // an AMD load request. Those cases could generate an error when an
+ // anonymous define() is called outside of a loader request.
+ if (true) {
+ !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function() {
+ return _;
+ }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
+ __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+ }
+}.call(this));
- var later = function() {
- previous = options.leading === false ? 0 : _.now();
- timeout = null;
- result = func.apply(context, args);
- if (!timeout) context = args = null;
- };
- var throttled = function() {
- var now = _.now();
- if (!previous && options.leading === false) previous = now;
- var remaining = wait - (now - previous);
- context = this;
- args = arguments;
- if (remaining <= 0 || remaining > wait) {
- if (timeout) {
- clearTimeout(timeout);
- timeout = null;
- }
- previous = now;
- result = func.apply(context, args);
- if (!timeout) context = args = null;
- } else if (!timeout && options.trailing !== false) {
- timeout = setTimeout(later, remaining);
- }
- return result;
- };
+/***/ }),
- throttled.cancel = function() {
- clearTimeout(timeout);
- previous = 0;
- timeout = context = args = null;
- };
+/***/ "./node_modules/backbone/backbone.js":
+/*!*******************************************!*\
+ !*** ./node_modules/backbone/backbone.js ***!
+ \*******************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
- return throttled;
- };
+/* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Backbone.js 1.3.3
- // Returns a function, that, as long as it continues to be invoked, will not
- // be triggered. The function will be called after it stops being called for
- // N milliseconds. If `immediate` is passed, trigger the function on the
- // leading edge, instead of the trailing.
- _.debounce = function(func, wait, immediate) {
- var timeout, result;
+// (c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+// Backbone may be freely distributed under the MIT license.
+// For all details and documentation:
+// http://backbonejs.org
- var later = function(context, args) {
- timeout = null;
- if (args) result = func.apply(context, args);
- };
+(function(factory) {
- var debounced = restArguments(function(args) {
- if (timeout) clearTimeout(timeout);
- if (immediate) {
- var callNow = !timeout;
- timeout = setTimeout(later, wait);
- if (callNow) result = func.apply(this, args);
- } else {
- timeout = _.delay(later, wait, this, args);
- }
+ // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
+ // We use `self` instead of `window` for `WebWorker` support.
+ var root = (typeof self == 'object' && self.self === self && self) ||
+ (typeof global == 'object' && global.global === global && global);
- return result;
- });
+ // Set up Backbone appropriately for the environment. Start with AMD.
+ if (true) {
+ !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(/*! underscore */ "./node_modules/underscore/underscore.js"), __webpack_require__(/*! jquery */ "./node_modules/cash-dom/dist/cash.js"), exports], __WEBPACK_AMD_DEFINE_RESULT__ = (function(_, $, exports) {
+ // Export global even in AMD case in case this script is loaded with
+ // others that may still expect a global Backbone.
+ root.Backbone = factory(root, exports, _, $);
+ }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
+ __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
- debounced.cancel = function() {
- clearTimeout(timeout);
- timeout = null;
- };
+ // Next for Node.js or CommonJS. jQuery may not be needed as a module.
+ } else { var _, $; }
- return debounced;
- };
+})(function(root, Backbone, _, $) {
- // Returns the first function passed as an argument to the second,
- // allowing you to adjust arguments, run code before and after, and
- // conditionally execute the original function.
- _.wrap = function(func, wrapper) {
- return _.partial(wrapper, func);
- };
+ // Initial Setup
+ // -------------
- // Returns a negated version of the passed-in predicate.
- _.negate = function(predicate) {
- return function() {
- return !predicate.apply(this, arguments);
- };
- };
+ // Save the previous value of the `Backbone` variable, so that it can be
+ // restored later on, if `noConflict` is used.
+ var previousBackbone = root.Backbone;
- // Returns a function that is the composition of a list of functions, each
- // consuming the return value of the function that follows.
- _.compose = function() {
- var args = arguments;
- var start = args.length - 1;
- return function() {
- var i = start;
- var result = args[start].apply(this, arguments);
- while (i--) result = args[i].call(this, result);
- return result;
- };
- };
+ // Create a local reference to a common array method we'll want to use later.
+ var slice = Array.prototype.slice;
- // Returns a function that will only be executed on and after the Nth call.
- _.after = function(times, func) {
- return function() {
- if (--times < 1) {
- return func.apply(this, arguments);
- }
- };
- };
+ // Current version of the library. Keep in sync with `package.json`.
+ Backbone.VERSION = '1.3.3';
- // Returns a function that will only be executed up to (but not including) the Nth call.
- _.before = function(times, func) {
- var memo;
- return function() {
- if (--times > 0) {
- memo = func.apply(this, arguments);
- }
- if (times <= 1) func = null;
- return memo;
- };
+ // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
+ // the `$` variable.
+ Backbone.$ = $;
+
+ // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
+ // to its previous owner. Returns a reference to this Backbone object.
+ Backbone.noConflict = function() {
+ root.Backbone = previousBackbone;
+ return this;
};
- // Returns a function that will be executed at most one time, no matter how
- // often you call it. Useful for lazy initialization.
- _.once = _.partial(_.before, 2);
+ // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
+ // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
+ // set a `X-Http-Method-Override` header.
+ Backbone.emulateHTTP = false;
- _.restArguments = restArguments;
+ // Turn on `emulateJSON` to support legacy servers that can't deal with direct
+ // `application/json` requests ... this will encode the body as
+ // `application/x-www-form-urlencoded` instead and will send the model in a
+ // form param named `model`.
+ Backbone.emulateJSON = false;
- // Object Functions
- // ----------------
+ // Proxy Backbone class methods to Underscore functions, wrapping the model's
+ // `attributes` object or collection's `models` array behind the scenes.
+ //
+ // collection.filter(function(model) { return model.get('age') > 10 });
+ // collection.each(this.addView);
+ //
+ // `Function#apply` can be slow so we use the method's arg count, if we know it.
+ var addMethod = function(length, method, attribute) {
+ switch (length) {
+ case 1: return function() {
+ return _[method](this[attribute]);
+ };
+ case 2: return function(value) {
+ return _[method](this[attribute], value);
+ };
+ case 3: return function(iteratee, context) {
+ return _[method](this[attribute], cb(iteratee, this), context);
+ };
+ case 4: return function(iteratee, defaultVal, context) {
+ return _[method](this[attribute], cb(iteratee, this), defaultVal, context);
+ };
+ default: return function() {
+ var args = slice.call(arguments);
+ args.unshift(this[attribute]);
+ return _[method].apply(_, args);
+ };
+ }
+ };
+ var addUnderscoreMethods = function(Class, methods, attribute) {
+ _.each(methods, function(length, method) {
+ if (_[method]) Class.prototype[method] = addMethod(length, method, attribute);
+ });
+ };
- // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
- var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
- var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
- 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
+ // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`.
+ var cb = function(iteratee, instance) {
+ if (_.isFunction(iteratee)) return iteratee;
+ if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee);
+ if (_.isString(iteratee)) return function(model) { return model.get(iteratee); };
+ return iteratee;
+ };
+ var modelMatcher = function(attrs) {
+ var matcher = _.matches(attrs);
+ return function(model) {
+ return matcher(model.attributes);
+ };
+ };
- var collectNonEnumProps = function(obj, keys) {
- var nonEnumIdx = nonEnumerableProps.length;
- var constructor = obj.constructor;
- var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;
+ // Backbone.Events
+ // ---------------
- // Constructor is a special case.
- var prop = 'constructor';
- if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
+ // A module that can be mixed in to *any object* in order to provide it with
+ // a custom event channel. You may bind a callback to an event with `on` or
+ // remove with `off`; `trigger`-ing an event fires all callbacks in
+ // succession.
+ //
+ // var object = {};
+ // _.extend(object, Backbone.Events);
+ // object.on('expand', function(){ alert('expanded'); });
+ // object.trigger('expand');
+ //
+ var Events = Backbone.Events = {};
- while (nonEnumIdx--) {
- prop = nonEnumerableProps[nonEnumIdx];
- if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
- keys.push(prop);
+ // Regular expression used to split event strings.
+ var eventSplitter = /\s+/;
+
+ // Iterates over the standard `event, callback` (as well as the fancy multiple
+ // space-separated events `"change blur", callback` and jQuery-style event
+ // maps `{event: callback}`).
+ var eventsApi = function(iteratee, events, name, callback, opts) {
+ var i = 0, names;
+ if (name && typeof name === 'object') {
+ // Handle event maps.
+ if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
+ for (names = _.keys(name); i < names.length ; i++) {
+ events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
}
+ } else if (name && eventSplitter.test(name)) {
+ // Handle space-separated event names by delegating them individually.
+ for (names = name.split(eventSplitter); i < names.length; i++) {
+ events = iteratee(events, names[i], callback, opts);
+ }
+ } else {
+ // Finally, standard events.
+ events = iteratee(events, name, callback, opts);
}
+ return events;
};
- // Retrieve the names of an object's own properties.
- // Delegates to **ECMAScript 5**'s native `Object.keys`.
- _.keys = function(obj) {
- if (!_.isObject(obj)) return [];
- if (nativeKeys) return nativeKeys(obj);
- var keys = [];
- for (var key in obj) if (_.has(obj, key)) keys.push(key);
- // Ahem, IE < 9.
- if (hasEnumBug) collectNonEnumProps(obj, keys);
- return keys;
- };
-
- // Retrieve all the property names of an object.
- _.allKeys = function(obj) {
- if (!_.isObject(obj)) return [];
- var keys = [];
- for (var key in obj) keys.push(key);
- // Ahem, IE < 9.
- if (hasEnumBug) collectNonEnumProps(obj, keys);
- return keys;
+ // Bind an event to a `callback` function. Passing `"all"` will bind
+ // the callback to all events fired.
+ Events.on = function(name, callback, context) {
+ return internalOn(this, name, callback, context);
};
- // Retrieve the values of an object's properties.
- _.values = function(obj) {
- var keys = _.keys(obj);
- var length = keys.length;
- var values = Array(length);
- for (var i = 0; i < length; i++) {
- values[i] = obj[keys[i]];
- }
- return values;
- };
+ // Guard the `listening` argument from the public API.
+ var internalOn = function(obj, name, callback, context, listening) {
+ obj._events = eventsApi(onApi, obj._events || {}, name, callback, {
+ context: context,
+ ctx: obj,
+ listening: listening
+ });
- // Returns the results of applying the iteratee to each element of the object.
- // In contrast to _.map it returns an object.
- _.mapObject = function(obj, iteratee, context) {
- iteratee = cb(iteratee, context);
- var keys = _.keys(obj),
- length = keys.length,
- results = {};
- for (var index = 0; index < length; index++) {
- var currentKey = keys[index];
- results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
+ if (listening) {
+ var listeners = obj._listeners || (obj._listeners = {});
+ listeners[listening.id] = listening;
}
- return results;
- };
- // Convert an object into a list of `[key, value]` pairs.
- // The opposite of _.object.
- _.pairs = function(obj) {
- var keys = _.keys(obj);
- var length = keys.length;
- var pairs = Array(length);
- for (var i = 0; i < length; i++) {
- pairs[i] = [keys[i], obj[keys[i]]];
- }
- return pairs;
+ return obj;
};
- // Invert the keys and values of an object. The values must be serializable.
- _.invert = function(obj) {
- var result = {};
- var keys = _.keys(obj);
- for (var i = 0, length = keys.length; i < length; i++) {
- result[obj[keys[i]]] = keys[i];
- }
- return result;
- };
+ // Inversion-of-control versions of `on`. Tell *this* object to listen to
+ // an event in another object... keeping track of what it's listening to
+ // for easier unbinding later.
+ Events.listenTo = function(obj, name, callback) {
+ if (!obj) return this;
+ var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
+ var listeningTo = this._listeningTo || (this._listeningTo = {});
+ var listening = listeningTo[id];
- // Return a sorted list of the function names available on the object.
- // Aliased as `methods`.
- _.functions = _.methods = function(obj) {
- var names = [];
- for (var key in obj) {
- if (_.isFunction(obj[key])) names.push(key);
+ // This object is not listening to any other events on `obj` yet.
+ // Setup the necessary references to track the listening callbacks.
+ if (!listening) {
+ var thisId = this._listenId || (this._listenId = _.uniqueId('l'));
+ listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0};
}
- return names.sort();
- };
- // An internal function for creating assigner functions.
- var createAssigner = function(keysFunc, defaults) {
- return function(obj) {
- var length = arguments.length;
- if (defaults) obj = Object(obj);
- if (length < 2 || obj == null) return obj;
- for (var index = 1; index < length; index++) {
- var source = arguments[index],
- keys = keysFunc(source),
- l = keys.length;
- for (var i = 0; i < l; i++) {
- var key = keys[i];
- if (!defaults || obj[key] === void 0) obj[key] = source[key];
- }
- }
- return obj;
- };
+ // Bind callbacks on obj, and keep track of them on listening.
+ internalOn(obj, name, callback, this, listening);
+ return this;
};
- // Extend a given object with all the properties in passed-in object(s).
- _.extend = createAssigner(_.allKeys);
-
- // Assigns a given object with all the own properties in the passed-in object(s).
- // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
- _.extendOwn = _.assign = createAssigner(_.keys);
+ // The reducing API that adds a callback to the `events` object.
+ var onApi = function(events, name, callback, options) {
+ if (callback) {
+ var handlers = events[name] || (events[name] = []);
+ var context = options.context, ctx = options.ctx, listening = options.listening;
+ if (listening) listening.count++;
- // Returns the first key on an object that passes a predicate test.
- _.findKey = function(obj, predicate, context) {
- predicate = cb(predicate, context);
- var keys = _.keys(obj), key;
- for (var i = 0, length = keys.length; i < length; i++) {
- key = keys[i];
- if (predicate(obj[key], key, obj)) return key;
+ handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening});
}
+ return events;
};
- // Internal pick helper function to determine if `obj` has key `key`.
- var keyInObj = function(value, key, obj) {
- return key in obj;
+ // Remove one or many callbacks. If `context` is null, removes all
+ // callbacks with that function. If `callback` is null, removes all
+ // callbacks for the event. If `name` is null, removes all bound
+ // callbacks for all events.
+ Events.off = function(name, callback, context) {
+ if (!this._events) return this;
+ this._events = eventsApi(offApi, this._events, name, callback, {
+ context: context,
+ listeners: this._listeners
+ });
+ return this;
};
- // Return a copy of the object only containing the whitelisted properties.
- _.pick = restArguments(function(obj, keys) {
- var result = {}, iteratee = keys[0];
- if (obj == null) return result;
- if (_.isFunction(iteratee)) {
- if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
- keys = _.allKeys(obj);
- } else {
- iteratee = keyInObj;
- keys = flatten(keys, false, false);
- obj = Object(obj);
- }
- for (var i = 0, length = keys.length; i < length; i++) {
- var key = keys[i];
- var value = obj[key];
- if (iteratee(value, key, obj)) result[key] = value;
- }
- return result;
- });
-
- // Return a copy of the object without the blacklisted properties.
- _.omit = restArguments(function(obj, keys) {
- var iteratee = keys[0], context;
- if (_.isFunction(iteratee)) {
- iteratee = _.negate(iteratee);
- if (keys.length > 1) context = keys[1];
- } else {
- keys = _.map(flatten(keys, false, false), String);
- iteratee = function(value, key) {
- return !_.contains(keys, key);
- };
- }
- return _.pick(obj, iteratee, context);
- });
-
- // Fill in a given object with default properties.
- _.defaults = createAssigner(_.allKeys, true);
+ // Tell this object to stop listening to either specific events ... or
+ // to every object it's currently listening to.
+ Events.stopListening = function(obj, name, callback) {
+ var listeningTo = this._listeningTo;
+ if (!listeningTo) return this;
- // Creates an object that inherits from the given prototype object.
- // If additional properties are provided then they will be added to the
- // created object.
- _.create = function(prototype, props) {
- var result = baseCreate(prototype);
- if (props) _.extendOwn(result, props);
- return result;
- };
+ var ids = obj ? [obj._listenId] : _.keys(listeningTo);
- // Create a (shallow-cloned) duplicate of an object.
- _.clone = function(obj) {
- if (!_.isObject(obj)) return obj;
- return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
- };
+ for (var i = 0; i < ids.length; i++) {
+ var listening = listeningTo[ids[i]];
- // Invokes interceptor with the obj, and then returns obj.
- // The primary purpose of this method is to "tap into" a method chain, in
- // order to perform operations on intermediate results within the chain.
- _.tap = function(obj, interceptor) {
- interceptor(obj);
- return obj;
- };
+ // If listening doesn't exist, this object is not currently
+ // listening to obj. Break out early.
+ if (!listening) break;
- // Returns whether an object has a given set of `key:value` pairs.
- _.isMatch = function(object, attrs) {
- var keys = _.keys(attrs), length = keys.length;
- if (object == null) return !length;
- var obj = Object(object);
- for (var i = 0; i < length; i++) {
- var key = keys[i];
- if (attrs[key] !== obj[key] || !(key in obj)) return false;
+ listening.obj.off(name, callback, this);
}
- return true;
- };
-
- // Internal recursive comparison function for `isEqual`.
- var eq, deepEq;
- eq = function(a, b, aStack, bStack) {
- // Identical objects are equal. `0 === -0`, but they aren't identical.
- // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
- if (a === b) return a !== 0 || 1 / a === 1 / b;
- // `null` or `undefined` only equal to itself (strict comparison).
- if (a == null || b == null) return false;
- // `NaN`s are equivalent, but non-reflexive.
- if (a !== a) return b !== b;
- // Exhaust primitive checks
- var type = typeof a;
- if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
- return deepEq(a, b, aStack, bStack);
+ return this;
};
- // Internal recursive comparison function for `isEqual`.
- deepEq = function(a, b, aStack, bStack) {
- // Unwrap any wrapped objects.
- if (a instanceof _) a = a._wrapped;
- if (b instanceof _) b = b._wrapped;
- // Compare `[[Class]]` names.
- var className = toString.call(a);
- if (className !== toString.call(b)) return false;
- switch (className) {
- // Strings, numbers, regular expressions, dates, and booleans are compared by value.
- case '[object RegExp]':
- // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
- case '[object String]':
- // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
- // equivalent to `new String("5")`.
- return '' + a === '' + b;
- case '[object Number]':
- // `NaN`s are equivalent, but non-reflexive.
- // Object(NaN) is equivalent to NaN.
- if (+a !== +a) return +b !== +b;
- // An `egal` comparison is performed for other numeric values.
- return +a === 0 ? 1 / +a === 1 / b : +a === +b;
- case '[object Date]':
- case '[object Boolean]':
- // Coerce dates and booleans to numeric primitive values. Dates are compared by their
- // millisecond representations. Note that invalid dates with millisecond representations
- // of `NaN` are not equivalent.
- return +a === +b;
- case '[object Symbol]':
- return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
- }
+ // The reducing API that removes a callback from the `events` object.
+ var offApi = function(events, name, callback, options) {
+ if (!events) return;
- var areArrays = className === '[object Array]';
- if (!areArrays) {
- if (typeof a != 'object' || typeof b != 'object') return false;
+ var i = 0, listening;
+ var context = options.context, listeners = options.listeners;
- // Objects with different constructors are not equivalent, but `Object`s or `Array`s
- // from different frames are.
- var aCtor = a.constructor, bCtor = b.constructor;
- if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
- _.isFunction(bCtor) && bCtor instanceof bCtor)
- && ('constructor' in a && 'constructor' in b)) {
- return false;
+ // Delete all events listeners and "drop" events.
+ if (!name && !callback && !context) {
+ var ids = _.keys(listeners);
+ for (; i < ids.length; i++) {
+ listening = listeners[ids[i]];
+ delete listeners[listening.id];
+ delete listening.listeningTo[listening.objId];
}
+ return;
}
- // Assume equality for cyclic structures. The algorithm for detecting cyclic
- // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
- // Initializing stack of traversed objects.
- // It's done here since we only need them for objects and arrays comparison.
- aStack = aStack || [];
- bStack = bStack || [];
- var length = aStack.length;
- while (length--) {
- // Linear search. Performance is inversely proportional to the number of
- // unique nested structures.
- if (aStack[length] === a) return bStack[length] === b;
- }
+ var names = name ? [name] : _.keys(events);
+ for (; i < names.length; i++) {
+ name = names[i];
+ var handlers = events[name];
- // Add the first object to the stack of traversed objects.
- aStack.push(a);
- bStack.push(b);
+ // Bail out if there are no events stored.
+ if (!handlers) break;
- // Recursively compare objects and arrays.
- if (areArrays) {
- // Compare array lengths to determine if a deep comparison is necessary.
- length = a.length;
- if (length !== b.length) return false;
- // Deep compare the contents, ignoring non-numeric properties.
- while (length--) {
- if (!eq(a[length], b[length], aStack, bStack)) return false;
+ // Replace events if there are any remaining. Otherwise, clean up.
+ var remaining = [];
+ for (var j = 0; j < handlers.length; j++) {
+ var handler = handlers[j];
+ if (
+ callback && callback !== handler.callback &&
+ callback !== handler.callback._callback ||
+ context && context !== handler.context
+ ) {
+ remaining.push(handler);
+ } else {
+ listening = handler.listening;
+ if (listening && --listening.count === 0) {
+ delete listeners[listening.id];
+ delete listening.listeningTo[listening.objId];
+ }
+ }
}
- } else {
- // Deep compare objects.
- var keys = _.keys(a), key;
- length = keys.length;
- // Ensure that both objects contain the same number of properties before comparing deep equality.
- if (_.keys(b).length !== length) return false;
- while (length--) {
- // Deep compare each member
- key = keys[length];
- if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
+
+ // Update tail event if the list has any events. Otherwise, clean up.
+ if (remaining.length) {
+ events[name] = remaining;
+ } else {
+ delete events[name];
}
}
- // Remove the first object from the stack of traversed objects.
- aStack.pop();
- bStack.pop();
- return true;
- };
-
- // Perform a deep comparison to check if two objects are equal.
- _.isEqual = function(a, b) {
- return eq(a, b);
+ return events;
};
- // Is a given array, string, or object empty?
- // An "empty" object has no enumerable own-properties.
- _.isEmpty = function(obj) {
- if (obj == null) return true;
- if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
- return _.keys(obj).length === 0;
+ // Bind an event to only be triggered a single time. After the first time
+ // the callback is invoked, its listener will be removed. If multiple events
+ // are passed in using the space-separated syntax, the handler will fire
+ // once for each event, not once for a combination of all events.
+ Events.once = function(name, callback, context) {
+ // Map the event into a `{event: once}` object.
+ var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this));
+ if (typeof name === 'string' && context == null) callback = void 0;
+ return this.on(events, callback, context);
};
- // Is a given value a DOM element?
- _.isElement = function(obj) {
- return !!(obj && obj.nodeType === 1);
+ // Inversion-of-control versions of `once`.
+ Events.listenToOnce = function(obj, name, callback) {
+ // Map the event into a `{event: once}` object.
+ var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj));
+ return this.listenTo(obj, events);
};
- // Is a given value an array?
- // Delegates to ECMA5's native Array.isArray
- _.isArray = nativeIsArray || function(obj) {
- return toString.call(obj) === '[object Array]';
+ // Reduces the event callbacks into a map of `{event: onceWrapper}`.
+ // `offer` unbinds the `onceWrapper` after it has been called.
+ var onceMap = function(map, name, callback, offer) {
+ if (callback) {
+ var once = map[name] = _.once(function() {
+ offer(name, once);
+ callback.apply(this, arguments);
+ });
+ once._callback = callback;
+ }
+ return map;
};
- // Is a given variable an object?
- _.isObject = function(obj) {
- var type = typeof obj;
- return type === 'function' || type === 'object' && !!obj;
- };
+ // Trigger one or many events, firing all bound callbacks. Callbacks are
+ // passed the same arguments as `trigger` is, apart from the event name
+ // (unless you're listening on `"all"`, which will cause your callback to
+ // receive the true name of the event as the first argument).
+ Events.trigger = function(name) {
+ if (!this._events) return this;
- // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet.
- _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {
- _['is' + name] = function(obj) {
- return toString.call(obj) === '[object ' + name + ']';
- };
- });
+ var length = Math.max(0, arguments.length - 1);
+ var args = Array(length);
+ for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
- // Define a fallback version of the method in browsers (ahem, IE < 9), where
- // there isn't any inspectable "Arguments" type.
- if (!_.isArguments(arguments)) {
- _.isArguments = function(obj) {
- return _.has(obj, 'callee');
- };
- }
+ eventsApi(triggerApi, this._events, name, void 0, args);
+ return this;
+ };
- // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
- // IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).
- var nodelist = root.document && root.document.childNodes;
- if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {
- _.isFunction = function(obj) {
- return typeof obj == 'function' || false;
- };
- }
+ // Handles triggering the appropriate event callbacks.
+ var triggerApi = function(objEvents, name, callback, args) {
+ if (objEvents) {
+ var events = objEvents[name];
+ var allEvents = objEvents.all;
+ if (events && allEvents) allEvents = allEvents.slice();
+ if (events) triggerEvents(events, args);
+ if (allEvents) triggerEvents(allEvents, [name].concat(args));
+ }
+ return objEvents;
+ };
- // Is a given object a finite number?
- _.isFinite = function(obj) {
- return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj));
+ // A difficult-to-believe, but optimized internal dispatch function for
+ // triggering events. Tries to keep the usual cases speedy (most internal
+ // Backbone events have 3 arguments).
+ var triggerEvents = function(events, args) {
+ var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
+ switch (args.length) {
+ case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
+ case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
+ case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
+ case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
+ default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
+ }
};
- // Is the given value `NaN`?
- _.isNaN = function(obj) {
- return _.isNumber(obj) && isNaN(obj);
- };
+ // Aliases for backwards compatibility.
+ Events.bind = Events.on;
+ Events.unbind = Events.off;
- // Is a given value a boolean?
- _.isBoolean = function(obj) {
- return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
- };
+ // Allow the `Backbone` object to serve as a global event bus, for folks who
+ // want global "pubsub" in a convenient place.
+ _.extend(Backbone, Events);
- // Is a given value equal to null?
- _.isNull = function(obj) {
- return obj === null;
- };
+ // Backbone.Model
+ // --------------
- // Is a given variable undefined?
- _.isUndefined = function(obj) {
- return obj === void 0;
- };
+ // Backbone **Models** are the basic data object in the framework --
+ // frequently representing a row in a table in a database on your server.
+ // A discrete chunk of data and a bunch of useful, related methods for
+ // performing computations and transformations on that data.
- // Shortcut function for checking if an object has a given property directly
- // on itself (in other words, not on a prototype).
- _.has = function(obj, path) {
- if (!_.isArray(path)) {
- return obj != null && hasOwnProperty.call(obj, path);
- }
- var length = path.length;
- for (var i = 0; i < length; i++) {
- var key = path[i];
- if (obj == null || !hasOwnProperty.call(obj, key)) {
- return false;
- }
- obj = obj[key];
- }
- return !!length;
+ // Create a new model with the specified attributes. A client id (`cid`)
+ // is automatically generated and assigned for you.
+ var Model = Backbone.Model = function(attributes, options) {
+ var attrs = attributes || {};
+ options || (options = {});
+ this.cid = _.uniqueId(this.cidPrefix);
+ this.attributes = {};
+ if (options.collection) this.collection = options.collection;
+ if (options.parse) attrs = this.parse(attrs, options) || {};
+ var defaults = _.result(this, 'defaults');
+ attrs = _.defaults(_.extend({}, defaults, attrs), defaults);
+ this.set(attrs, options);
+ this.changed = {};
+ this.initialize.apply(this, arguments);
};
- // Utility Functions
- // -----------------
+ // Attach all inheritable methods to the Model prototype.
+ _.extend(Model.prototype, Events, {
- // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
- // previous owner. Returns a reference to the Underscore object.
- _.noConflict = function() {
- root._ = previousUnderscore;
- return this;
- };
+ // A hash of attributes whose current and previous value differ.
+ changed: null,
- // Keep the identity function around for default iteratees.
- _.identity = function(value) {
- return value;
- };
+ // The value returned during the last failed validation.
+ validationError: null,
- // Predicate-generating functions. Often useful outside of Underscore.
- _.constant = function(value) {
- return function() {
- return value;
- };
- };
+ // The default name for the JSON `id` attribute is `"id"`. MongoDB and
+ // CouchDB users may want to set this to `"_id"`.
+ idAttribute: 'id',
- _.noop = function(){};
+ // The prefix is used to create the client id which is used to identify models locally.
+ // You may want to override this if you're experiencing name clashes with model ids.
+ cidPrefix: 'c',
- // Creates a function that, when passed an object, will traverse that object’s
- // properties down the given `path`, specified as an array of keys or indexes.
- _.property = function(path) {
- if (!_.isArray(path)) {
- return shallowProperty(path);
- }
- return function(obj) {
- return deepGet(obj, path);
- };
- };
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize: function(){},
- // Generates a function for a given object that returns a given property.
- _.propertyOf = function(obj) {
- if (obj == null) {
- return function(){};
- }
- return function(path) {
- return !_.isArray(path) ? obj[path] : deepGet(obj, path);
- };
- };
+ // Return a copy of the model's `attributes` object.
+ toJSON: function(options) {
+ return _.clone(this.attributes);
+ },
- // Returns a predicate for checking whether an object has a given set of
- // `key:value` pairs.
- _.matcher = _.matches = function(attrs) {
- attrs = _.extendOwn({}, attrs);
- return function(obj) {
- return _.isMatch(obj, attrs);
- };
- };
+ // Proxy `Backbone.sync` by default -- but override this if you need
+ // custom syncing semantics for *this* particular model.
+ sync: function() {
+ return Backbone.sync.apply(this, arguments);
+ },
- // Run a function **n** times.
- _.times = function(n, iteratee, context) {
- var accum = Array(Math.max(0, n));
- iteratee = optimizeCb(iteratee, context, 1);
- for (var i = 0; i < n; i++) accum[i] = iteratee(i);
- return accum;
- };
+ // Get the value of an attribute.
+ get: function(attr) {
+ return this.attributes[attr];
+ },
- // Return a random integer between min and max (inclusive).
- _.random = function(min, max) {
- if (max == null) {
- max = min;
- min = 0;
- }
- return min + Math.floor(Math.random() * (max - min + 1));
- };
+ // Get the HTML-escaped value of an attribute.
+ escape: function(attr) {
+ return _.escape(this.get(attr));
+ },
- // A (possibly faster) way to get the current timestamp as an integer.
- _.now = Date.now || function() {
- return new Date().getTime();
- };
+ // Returns `true` if the attribute contains a value that is not null
+ // or undefined.
+ has: function(attr) {
+ return this.get(attr) != null;
+ },
- // List of HTML entities for escaping.
- var escapeMap = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": ''',
- '`': '`'
- };
- var unescapeMap = _.invert(escapeMap);
+ // Special-cased proxy to underscore's `_.matches` method.
+ matches: function(attrs) {
+ return !!_.iteratee(attrs, this)(this.attributes);
+ },
- // Functions for escaping and unescaping strings to/from HTML interpolation.
- var createEscaper = function(map) {
- var escaper = function(match) {
- return map[match];
- };
- // Regexes for identifying a key that needs to be escaped.
- var source = '(?:' + _.keys(map).join('|') + ')';
- var testRegexp = RegExp(source);
- var replaceRegexp = RegExp(source, 'g');
- return function(string) {
- string = string == null ? '' : '' + string;
- return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
- };
- };
- _.escape = createEscaper(escapeMap);
- _.unescape = createEscaper(unescapeMap);
+ // Set a hash of model attributes on the object, firing `"change"`. This is
+ // the core primitive operation of a model, updating the data and notifying
+ // anyone who needs to know about the change in state. The heart of the beast.
+ set: function(key, val, options) {
+ if (key == null) return this;
- // Traverses the children of `obj` along `path`. If a child is a function, it
- // is invoked with its parent as context. Returns the value of the final
- // child, or `fallback` if any child is undefined.
- _.result = function(obj, path, fallback) {
- if (!_.isArray(path)) path = [path];
- var length = path.length;
- if (!length) {
- return _.isFunction(fallback) ? fallback.call(obj) : fallback;
- }
- for (var i = 0; i < length; i++) {
- var prop = obj == null ? void 0 : obj[path[i]];
- if (prop === void 0) {
- prop = fallback;
- i = length; // Ensure we don't continue iterating.
+ // Handle both `"key", value` and `{key: value}` -style arguments.
+ var attrs;
+ if (typeof key === 'object') {
+ attrs = key;
+ options = val;
+ } else {
+ (attrs = {})[key] = val;
}
- obj = _.isFunction(prop) ? prop.call(obj) : prop;
- }
- return obj;
- };
-
- // Generate a unique integer id (unique within the entire client session).
- // Useful for temporary DOM ids.
- var idCounter = 0;
- _.uniqueId = function(prefix) {
- var id = ++idCounter + '';
- return prefix ? prefix + id : id;
- };
- // By default, Underscore uses ERB-style template delimiters, change the
- // following template settings to use alternative delimiters.
- _.templateSettings = {
- evaluate: /<%([\s\S]+?)%>/g,
- interpolate: /<%=([\s\S]+?)%>/g,
- escape: /<%-([\s\S]+?)%>/g
- };
+ options || (options = {});
- // When customizing `templateSettings`, if you don't want to define an
- // interpolation, evaluation or escaping regex, we need one that is
- // guaranteed not to match.
- var noMatch = /(.)^/;
+ // Run validation.
+ if (!this._validate(attrs, options)) return false;
- // Certain characters need to be escaped so that they can be put into a
- // string literal.
- var escapes = {
- "'": "'",
- '\\': '\\',
- '\r': 'r',
- '\n': 'n',
- '\u2028': 'u2028',
- '\u2029': 'u2029'
- };
+ // Extract attributes and options.
+ var unset = options.unset;
+ var silent = options.silent;
+ var changes = [];
+ var changing = this._changing;
+ this._changing = true;
- var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
+ if (!changing) {
+ this._previousAttributes = _.clone(this.attributes);
+ this.changed = {};
+ }
- var escapeChar = function(match) {
- return '\\' + escapes[match];
- };
+ var current = this.attributes;
+ var changed = this.changed;
+ var prev = this._previousAttributes;
- // JavaScript micro-templating, similar to John Resig's implementation.
- // Underscore templating handles arbitrary delimiters, preserves whitespace,
- // and correctly escapes quotes within interpolated code.
- // NB: `oldSettings` only exists for backwards compatibility.
- _.template = function(text, settings, oldSettings) {
- if (!settings && oldSettings) settings = oldSettings;
- settings = _.defaults({}, settings, _.templateSettings);
+ // For each `set` attribute, update or delete the current value.
+ for (var attr in attrs) {
+ val = attrs[attr];
+ if (!_.isEqual(current[attr], val)) changes.push(attr);
+ if (!_.isEqual(prev[attr], val)) {
+ changed[attr] = val;
+ } else {
+ delete changed[attr];
+ }
+ unset ? delete current[attr] : current[attr] = val;
+ }
- // Combine delimiters into one regular expression via alternation.
- var matcher = RegExp([
- (settings.escape || noMatch).source,
- (settings.interpolate || noMatch).source,
- (settings.evaluate || noMatch).source
- ].join('|') + '|$', 'g');
+ // Update the `id`.
+ if (this.idAttribute in attrs) this.id = this.get(this.idAttribute);
- // Compile the template source, escaping string literals appropriately.
- var index = 0;
- var source = "__p+='";
- text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
- source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
- index = offset + match.length;
+ // Trigger all relevant attribute changes.
+ if (!silent) {
+ if (changes.length) this._pending = options;
+ for (var i = 0; i < changes.length; i++) {
+ this.trigger('change:' + changes[i], this, current[changes[i]], options);
+ }
+ }
- if (escape) {
- source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
- } else if (interpolate) {
- source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
- } else if (evaluate) {
- source += "';\n" + evaluate + "\n__p+='";
+ // You might be wondering why there's a `while` loop here. Changes can
+ // be recursively nested within `"change"` events.
+ if (changing) return this;
+ if (!silent) {
+ while (this._pending) {
+ options = this._pending;
+ this._pending = false;
+ this.trigger('change', this, options);
+ }
}
+ this._pending = false;
+ this._changing = false;
+ return this;
+ },
- // Adobe VMs need the match returned to produce the correct offset.
- return match;
- });
- source += "';\n";
+ // Remove an attribute from the model, firing `"change"`. `unset` is a noop
+ // if the attribute doesn't exist.
+ unset: function(attr, options) {
+ return this.set(attr, void 0, _.extend({}, options, {unset: true}));
+ },
- // If a variable is not specified, place data values in local scope.
- if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
+ // Clear all attributes on the model, firing `"change"`.
+ clear: function(options) {
+ var attrs = {};
+ for (var key in this.attributes) attrs[key] = void 0;
+ return this.set(attrs, _.extend({}, options, {unset: true}));
+ },
- source = "var __t,__p='',__j=Array.prototype.join," +
- "print=function(){__p+=__j.call(arguments,'');};\n" +
- source + 'return __p;\n';
+ // Determine if the model has changed since the last `"change"` event.
+ // If you specify an attribute name, determine if that attribute has changed.
+ hasChanged: function(attr) {
+ if (attr == null) return !_.isEmpty(this.changed);
+ return _.has(this.changed, attr);
+ },
- var render;
- try {
- render = new Function(settings.variable || 'obj', '_', source);
- } catch (e) {
- e.source = source;
- throw e;
- }
+ // Return an object containing all the attributes that have changed, or
+ // false if there are no changed attributes. Useful for determining what
+ // parts of a view need to be updated and/or what attributes need to be
+ // persisted to the server. Unset attributes will be set to undefined.
+ // You can also pass an attributes object to diff against the model,
+ // determining if there *would be* a change.
+ changedAttributes: function(diff) {
+ if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
+ var old = this._changing ? this._previousAttributes : this.attributes;
+ var changed = {};
+ for (var attr in diff) {
+ var val = diff[attr];
+ if (_.isEqual(old[attr], val)) continue;
+ changed[attr] = val;
+ }
+ return _.size(changed) ? changed : false;
+ },
- var template = function(data) {
- return render.call(this, data, _);
- };
+ // Get the previous value of an attribute, recorded at the time the last
+ // `"change"` event was fired.
+ previous: function(attr) {
+ if (attr == null || !this._previousAttributes) return null;
+ return this._previousAttributes[attr];
+ },
- // Provide the compiled source as a convenience for precompilation.
- var argument = settings.variable || 'obj';
- template.source = 'function(' + argument + '){\n' + source + '}';
+ // Get all of the attributes of the model at the time of the previous
+ // `"change"` event.
+ previousAttributes: function() {
+ return _.clone(this._previousAttributes);
+ },
- return template;
- };
+ // Fetch the model from the server, merging the response with the model's
+ // local attributes. Any changed attributes will trigger a "change" event.
+ fetch: function(options) {
+ options = _.extend({parse: true}, options);
+ var model = this;
+ var success = options.success;
+ options.success = function(resp) {
+ var serverAttrs = options.parse ? model.parse(resp, options) : resp;
+ if (!model.set(serverAttrs, options)) return false;
+ if (success) success.call(options.context, model, resp, options);
+ model.trigger('sync', model, resp, options);
+ };
+ wrapError(this, options);
+ return this.sync('read', this, options);
+ },
- // Add a "chain" function. Start chaining a wrapped Underscore object.
- _.chain = function(obj) {
- var instance = _(obj);
- instance._chain = true;
- return instance;
- };
+ // Set a hash of model attributes, and sync the model to the server.
+ // If the server returns an attributes hash that differs, the model's
+ // state will be `set` again.
+ save: function(key, val, options) {
+ // Handle both `"key", value` and `{key: value}` -style arguments.
+ var attrs;
+ if (key == null || typeof key === 'object') {
+ attrs = key;
+ options = val;
+ } else {
+ (attrs = {})[key] = val;
+ }
- // OOP
- // ---------------
- // If Underscore is called as a function, it returns a wrapped object that
- // can be used OO-style. This wrapper holds altered versions of all the
- // underscore functions. Wrapped objects may be chained.
+ options = _.extend({validate: true, parse: true}, options);
+ var wait = options.wait;
- // Helper function to continue chaining intermediate results.
- var chainResult = function(instance, obj) {
- return instance._chain ? _(obj).chain() : obj;
- };
+ // If we're not waiting and attributes exist, save acts as
+ // `set(attr).save(null, opts)` with validation. Otherwise, check if
+ // the model will be valid when the attributes, if any, are set.
+ if (attrs && !wait) {
+ if (!this.set(attrs, options)) return false;
+ } else if (!this._validate(attrs, options)) {
+ return false;
+ }
- // Add your own custom functions to the Underscore object.
- _.mixin = function(obj) {
- _.each(_.functions(obj), function(name) {
- var func = _[name] = obj[name];
- _.prototype[name] = function() {
- var args = [this._wrapped];
- push.apply(args, arguments);
- return chainResult(this, func.apply(_, args));
+ // After a successful server-side save, the client is (optionally)
+ // updated with the server-side state.
+ var model = this;
+ var success = options.success;
+ var attributes = this.attributes;
+ options.success = function(resp) {
+ // Ensure attributes are restored during synchronous saves.
+ model.attributes = attributes;
+ var serverAttrs = options.parse ? model.parse(resp, options) : resp;
+ if (wait) serverAttrs = _.extend({}, attrs, serverAttrs);
+ if (serverAttrs && !model.set(serverAttrs, options)) return false;
+ if (success) success.call(options.context, model, resp, options);
+ model.trigger('sync', model, resp, options);
};
- });
- return _;
- };
+ wrapError(this, options);
- // Add all of the Underscore functions to the wrapper object.
- _.mixin(_);
+ // Set temporary attributes if `{wait: true}` to properly find new ids.
+ if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
- // Add all mutator Array functions to the wrapper.
- _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
- var method = ArrayProto[name];
- _.prototype[name] = function() {
- var obj = this._wrapped;
- method.apply(obj, arguments);
- if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
- return chainResult(this, obj);
- };
- });
+ var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
+ if (method === 'patch' && !options.attrs) options.attrs = attrs;
+ var xhr = this.sync(method, this, options);
- // Add all accessor Array functions to the wrapper.
- _.each(['concat', 'join', 'slice'], function(name) {
- var method = ArrayProto[name];
- _.prototype[name] = function() {
- return chainResult(this, method.apply(this._wrapped, arguments));
- };
- });
+ // Restore attributes.
+ this.attributes = attributes;
- // Extracts the result from a wrapped and chained object.
- _.prototype.value = function() {
- return this._wrapped;
- };
+ return xhr;
+ },
+
+ // Destroy this model on the server if it was already persisted.
+ // Optimistically removes the model from its collection, if it has one.
+ // If `wait: true` is passed, waits for the server to respond before removal.
+ destroy: function(options) {
+ options = options ? _.clone(options) : {};
+ var model = this;
+ var success = options.success;
+ var wait = options.wait;
- // Provide unwrapping proxy for some methods used in engine operations
- // such as arithmetic and JSON stringification.
- _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
+ var destroy = function() {
+ model.stopListening();
+ model.trigger('destroy', model, model.collection, options);
+ };
- _.prototype.toString = function() {
- return String(this._wrapped);
- };
+ options.success = function(resp) {
+ if (wait) destroy();
+ if (success) success.call(options.context, model, resp, options);
+ if (!model.isNew()) model.trigger('sync', model, resp, options);
+ };
- // AMD registration happens at the end for compatibility with AMD loaders
- // that may not enforce next-turn semantics on modules. Even though general
- // practice for AMD registration is to be anonymous, underscore registers
- // as a named module because, like jQuery, it is a base library that is
- // popular enough to be bundled in a third party lib, but not be part of
- // an AMD load request. Those cases could generate an error when an
- // anonymous define() is called outside of a loader request.
- if (true) {
- !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function() {
- return _;
- }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
- __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
- }
-}());
+ var xhr = false;
+ if (this.isNew()) {
+ _.defer(options.success);
+ } else {
+ wrapError(this, options);
+ xhr = this.sync('delete', this, options);
+ }
+ if (!wait) destroy();
+ return xhr;
+ },
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7), __webpack_require__(64)(module)))
+ // Default URL for the model's representation on the server -- if you're
+ // using Backbone's restful methods, override this to change the endpoint
+ // that will be called.
+ url: function() {
+ var base =
+ _.result(this, 'urlRoot') ||
+ _.result(this.collection, 'url') ||
+ urlError();
+ if (this.isNew()) return base;
+ var id = this.get(this.idAttribute);
+ return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
+ },
-/***/ }),
-/* 2 */
-/***/ (function(module, exports, __webpack_require__) {
+ // **parse** converts a response into the hash of attributes to be `set` on
+ // the model. The default implementation is just to pass the response along.
+ parse: function(resp, options) {
+ return resp;
+ },
-"use strict";
+ // Create a new model with identical attributes to this one.
+ clone: function() {
+ return new this.constructor(this.attributes);
+ },
+ // A model is new if it has never been saved to the server, and lacks an id.
+ isNew: function() {
+ return !this.has(this.idAttribute);
+ },
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.getUnitFromValue = exports.normalizeFloat = exports.shallowDiff = exports.getElement = exports.camelCase = exports.getModel = exports.matches = exports.upFirst = exports.hasDnd = exports.off = exports.on = undefined;
+ // Check if the model is currently in a valid state.
+ isValid: function(options) {
+ return this._validate({}, _.extend({}, options, {validate: true}));
+ },
-var _underscore = __webpack_require__(1);
+ // Run validation against the next complete set of model attributes,
+ // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
+ _validate: function(attrs, options) {
+ if (!options.validate || !this.validate) return true;
+ attrs = _.extend({}, this.attributes, attrs);
+ var error = this.validationError = this.validate(attrs, options) || null;
+ if (!error) return true;
+ this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
+ return false;
+ }
-var elProt = window.Element.prototype;
-var matches = elProt.matches || elProt.webkitMatchesSelector || elProt.mozMatchesSelector || elProt.msMatchesSelector;
+ });
-/**
- * Returns shallow diff between 2 objects
- * @param {Object} objOrig
- * @param {Objec} objNew
- * @return {Object}
- * @example
- * var a = {foo: 'bar', baz: 1, faz: 'sop'};
- * var b = {foo: 'bar', baz: 2, bar: ''};
- * shallowDiff(a, b);
- * // -> {baz: 2, faz: null, bar: ''};
- */
-var shallowDiff = function shallowDiff(objOrig, objNew) {
- var result = {};
- var keysNew = (0, _underscore.keys)(objNew);
+ // Underscore methods that we want to implement on the Model, mapped to the
+ // number of arguments they take.
+ var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
+ omit: 0, chain: 1, isEmpty: 1};
- for (var prop in objOrig) {
- if (objOrig.hasOwnProperty(prop)) {
- var origValue = objOrig[prop];
- var newValue = objNew[prop];
+ // Mix in each Underscore method as a proxy to `Model#attributes`.
+ addUnderscoreMethods(Model, modelMethods, 'attributes');
- if (keysNew.indexOf(prop) >= 0) {
- if (origValue !== newValue) {
- result[prop] = newValue;
- }
- } else {
- result[prop] = null;
- }
- }
- }
+ // Backbone.Collection
+ // -------------------
- for (var _prop in objNew) {
- if (objNew.hasOwnProperty(_prop)) {
- if ((0, _underscore.isUndefined)(objOrig[_prop])) {
- result[_prop] = objNew[_prop];
- }
- }
- }
+ // If models tend to represent a single row of data, a Backbone Collection is
+ // more analogous to a table full of data ... or a small slice or page of that
+ // table, or a collection of rows that belong together for a particular reason
+ // -- all of the messages in this particular folder, all of the documents
+ // belonging to this particular author, and so on. Collections maintain
+ // indexes of their models, both in order, and for lookup by `id`.
- return result;
-};
+ // Create a new **Collection**, perhaps to contain a specific type of `model`.
+ // If a `comparator` is specified, the Collection will maintain
+ // its models in sort order, as they're added and removed.
+ var Collection = Backbone.Collection = function(models, options) {
+ options || (options = {});
+ if (options.model) this.model = options.model;
+ if (options.comparator !== void 0) this.comparator = options.comparator;
+ this._reset();
+ this.initialize.apply(this, arguments);
+ if (models) this.reset(models, _.extend({silent: true}, options));
+ };
-var on = function on(el, ev, fn) {
- ev = ev.split(/\s+/);
- el = el instanceof Array ? el : [el];
+ // Default options for `Collection#set`.
+ var setOptions = {add: true, remove: true, merge: true};
+ var addOptions = {add: true, remove: false};
- var _loop = function _loop(i) {
- el.forEach(function (elem) {
- return elem.addEventListener(ev[i], fn);
- });
+ // Splices `insert` into `array` at index `at`.
+ var splice = function(array, insert, at) {
+ at = Math.min(Math.max(at, 0), array.length);
+ var tail = Array(array.length - at);
+ var length = insert.length;
+ var i;
+ for (i = 0; i < tail.length; i++) tail[i] = array[i + at];
+ for (i = 0; i < length; i++) array[i + at] = insert[i];
+ for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
};
- for (var i = 0; i < ev.length; ++i) {
- _loop(i);
- }
-};
+ // Define the Collection's inheritable methods.
+ _.extend(Collection.prototype, Events, {
-var off = function off(el, ev, fn) {
- ev = ev.split(/\s+/);
- el = el instanceof Array ? el : [el];
+ // The default model for a collection is just a **Backbone.Model**.
+ // This should be overridden in most cases.
+ model: Model,
- var _loop2 = function _loop2(i) {
- el.forEach(function (elem) {
- return elem.removeEventListener(ev[i], fn);
- });
- };
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize: function(){},
- for (var i = 0; i < ev.length; ++i) {
- _loop2(i);
- }
-};
+ // The JSON representation of a Collection is an array of the
+ // models' attributes.
+ toJSON: function(options) {
+ return this.map(function(model) { return model.toJSON(options); });
+ },
-var getUnitFromValue = function getUnitFromValue(value) {
- return value.replace(parseFloat(value), '');
-};
+ // Proxy `Backbone.sync` by default.
+ sync: function() {
+ return Backbone.sync.apply(this, arguments);
+ },
-var upFirst = function upFirst(value) {
- return value[0].toUpperCase() + value.toLowerCase().slice(1);
-};
+ // Add a model, or list of models to the set. `models` may be Backbone
+ // Models or raw JavaScript objects to be converted to Models, or any
+ // combination of the two.
+ add: function(models, options) {
+ return this.set(models, _.extend({merge: false}, options, addOptions));
+ },
-var camelCase = function camelCase(value) {
- var values = value.split('-');
- return values[0].toLowerCase() + values.slice(1).map(upFirst);
-};
+ // Remove a model, or a list of models from the set.
+ remove: function(models, options) {
+ options = _.extend({}, options);
+ var singular = !_.isArray(models);
+ models = singular ? [models] : models.slice();
+ var removed = this._removeModels(models, options);
+ if (!options.silent && removed.length) {
+ options.changes = {added: [], merged: [], removed: removed};
+ this.trigger('update', this, options);
+ }
+ return singular ? removed[0] : removed;
+ },
-var normalizeFloat = function normalizeFloat(value) {
- var step = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
- var valueDef = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
+ // Update a collection by `set`-ing a new list of models, adding new ones,
+ // removing models that are no longer present, and merging models that
+ // already exist in the collection, as necessary. Similar to **Model#set**,
+ // the core operation for updating the data contained by the collection.
+ set: function(models, options) {
+ if (models == null) return;
- var stepDecimals = 0;
- if (isNaN(value)) return valueDef;
- value = parseFloat(value);
+ options = _.extend({}, setOptions, options);
+ if (options.parse && !this._isModel(models)) {
+ models = this.parse(models, options) || [];
+ }
- if (Math.floor(value) !== value) {
- var side = step.toString().split('.')[1];
- stepDecimals = side ? side.length : 0;
- }
+ var singular = !_.isArray(models);
+ models = singular ? [models] : models.slice();
- return stepDecimals ? parseFloat(value.toFixed(stepDecimals)) : value;
-};
+ var at = options.at;
+ if (at != null) at = +at;
+ if (at > this.length) at = this.length;
+ if (at < 0) at += this.length + 1;
-var hasDnd = function hasDnd(em) {
- return 'draggable' in document.createElement('i') && (em ? em.get('Config').nativeDnD : 1);
-};
+ var set = [];
+ var toAdd = [];
+ var toMerge = [];
+ var toRemove = [];
+ var modelMap = {};
-/**
- * Ensure to fetch the element from the input argument
- * @param {HTMLElement|Component} el Component or HTML element
- * @return {HTMLElement}
- */
-var getElement = function getElement(el) {
- if ((0, _underscore.isElement)(el)) {
- return el;
- } else if (el && el.getEl) {
- return el.getEl();
- }
-};
+ var add = options.add;
+ var merge = options.merge;
+ var remove = options.remove;
-/**
- * Ensure to fetch the model from the input argument
- * @param {HTMLElement|Component} el Component or HTML element
- * @return {Component}
- */
-var getModel = function getModel(el, $) {
- var model = el;
- (0, _underscore.isElement)(el) && (model = $(el).data('model'));
- return model;
-};
+ var sort = false;
+ var sortable = this.comparator && at == null && options.sort !== false;
+ var sortAttr = _.isString(this.comparator) ? this.comparator : null;
-exports.on = on;
-exports.off = off;
-exports.hasDnd = hasDnd;
-exports.upFirst = upFirst;
-exports.matches = matches;
-exports.getModel = getModel;
-exports.camelCase = camelCase;
-exports.getElement = getElement;
-exports.shallowDiff = shallowDiff;
-exports.normalizeFloat = normalizeFloat;
-exports.getUnitFromValue = getUnitFromValue;
+ // Turn bare objects into model references, and prevent invalid models
+ // from being added.
+ var model, i;
+ for (i = 0; i < models.length; i++) {
+ model = models[i];
-/***/ }),
-/* 3 */
-/***/ (function(module, exports, __webpack_require__) {
+ // If a duplicate is found, prevent it from being added and
+ // optionally merge it into the existing model.
+ var existing = this.get(model);
+ if (existing) {
+ if (merge && model !== existing) {
+ var attrs = this._isModel(model) ? model.attributes : model;
+ if (options.parse) attrs = existing.parse(attrs, options);
+ existing.set(attrs, options);
+ toMerge.push(existing);
+ if (sortable && !sort) sort = existing.hasChanged(sortAttr);
+ }
+ if (!modelMap[existing.cid]) {
+ modelMap[existing.cid] = true;
+ set.push(existing);
+ }
+ models[i] = existing;
-"use strict";
+ // If this is a new, valid model, push it to the `toAdd` list.
+ } else if (add) {
+ model = models[i] = this._prepareModel(model, options);
+ if (model) {
+ toAdd.push(model);
+ this._addReference(model, options);
+ modelMap[model.cid] = true;
+ set.push(model);
+ }
+ }
+ }
+
+ // Remove stale models.
+ if (remove) {
+ for (i = 0; i < this.length; i++) {
+ model = this.models[i];
+ if (!modelMap[model.cid]) toRemove.push(model);
+ }
+ if (toRemove.length) this._removeModels(toRemove, options);
+ }
+ // See if sorting is needed, update `length` and splice in new models.
+ var orderChanged = false;
+ var replace = !sortable && add && remove;
+ if (set.length && replace) {
+ orderChanged = this.length !== set.length || _.some(this.models, function(m, index) {
+ return m !== set[index];
+ });
+ this.models.length = 0;
+ splice(this.models, set, 0);
+ this.length = this.models.length;
+ } else if (toAdd.length) {
+ if (sortable) sort = true;
+ splice(this.models, toAdd, at == null ? this.length : at);
+ this.length = this.models.length;
+ }
-var _backbone = __webpack_require__(0);
+ // Silently sort the collection if appropriate.
+ if (sort) this.sort({silent: true});
-var _backbone2 = _interopRequireDefault(_backbone);
+ // Unless silenced, it's time to fire all appropriate add/sort/update events.
+ if (!options.silent) {
+ for (i = 0; i < toAdd.length; i++) {
+ if (at != null) options.index = at + i;
+ model = toAdd[i];
+ model.trigger('add', model, this, options);
+ }
+ if (sort || orderChanged) this.trigger('sort', this, options);
+ if (toAdd.length || toRemove.length || toMerge.length) {
+ options.changes = {
+ added: toAdd,
+ removed: toRemove,
+ merged: toMerge
+ };
+ this.trigger('update', this, options);
+ }
+ }
-var _underscore = __webpack_require__(1);
+ // Return the added (or merged) model (or models).
+ return singular ? models[0] : models;
+ },
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ // When you have more items than you want to add or remove individually,
+ // you can reset the entire set with a new list of models, without firing
+ // any granular `add` or `remove` events. Fires `reset` when finished.
+ // Useful for bulk operations and optimizations.
+ reset: function(models, options) {
+ options = options ? _.clone(options) : {};
+ for (var i = 0; i < this.models.length; i++) {
+ this._removeReference(this.models[i], options);
+ }
+ options.previousModels = this.models;
+ this._reset();
+ models = this.add(models, _.extend({silent: true}, options));
+ if (!options.silent) this.trigger('reset', this, options);
+ return models;
+ },
-var ComponentsView = __webpack_require__(52);
+ // Add a model to the end of the collection.
+ push: function(model, options) {
+ return this.add(model, _.extend({at: this.length}, options));
+ },
-module.exports = _backbone2.default.View.extend({
- className: function className() {
- return this.getClasses();
- },
- tagName: function tagName() {
- return this.model.get('tagName');
- },
- initialize: function initialize() {
- var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ // Remove a model from the end of the collection.
+ pop: function(options) {
+ var model = this.at(this.length - 1);
+ return this.remove(model, options);
+ },
- var model = this.model;
- var config = opt.config || {};
- this.opts = opt;
- this.config = config;
- this.em = config.em || '';
- this.pfx = config.stylePrefix || '';
- this.ppfx = config.pStylePrefix || '';
- this.attr = model.get('attributes');
- this.classe = this.attr.class || [];
- var $el = this.$el;
- var classes = model.get('classes');
- this.listenTo(model, 'change:style', this.updateStyle);
- this.listenTo(model, 'change:attributes', this.updateAttributes);
- this.listenTo(model, 'change:highlightable', this.updateHighlight);
- this.listenTo(model, 'change:status', this.updateStatus);
- this.listenTo(model, 'change:state', this.updateState);
- this.listenTo(model, 'change:script', this.render);
- this.listenTo(model, 'change:content', this.updateContent);
- this.listenTo(model, 'change', this.handleChange);
- this.listenTo(classes, 'add remove change', this.updateClasses);
- $el.data('model', model);
- $el.data('collection', model.get('components'));
- model.view = this;
- classes.length && this.importClasses();
- this.init();
- },
+ // Add a model to the beginning of the collection.
+ unshift: function(model, options) {
+ return this.add(model, _.extend({at: 0}, options));
+ },
+ // Remove a model from the beginning of the collection.
+ shift: function(options) {
+ var model = this.at(0);
+ return this.remove(model, options);
+ },
- /**
- * Initialize callback
- */
- init: function init() {},
+ // Slice out a sub-array of models from the collection.
+ slice: function() {
+ return slice.apply(this.models, arguments);
+ },
+ // Get a model from the set by id, cid, model object with id or cid
+ // properties, or an attributes object that is transformed through modelId.
+ get: function(obj) {
+ if (obj == null) return void 0;
+ return this._byId[obj] ||
+ this._byId[this.modelId(obj.attributes || obj)] ||
+ obj.cid && this._byId[obj.cid];
+ },
- /**
- * Handle any property change
- * @private
- */
- handleChange: function handleChange() {
- var model = this.model;
- model.emitUpdate();
+ // Returns `true` if the model is in the collection.
+ has: function(obj) {
+ return this.get(obj) != null;
+ },
- for (var prop in model.changed) {
- model.emitUpdate(prop);
- }
- },
+ // Get the model at the given index.
+ at: function(index) {
+ if (index < 0) index += this.length;
+ return this.models[index];
+ },
+
+ // Return models with matching attributes. Useful for simple cases of
+ // `filter`.
+ where: function(attrs, first) {
+ return this[first ? 'find' : 'filter'](attrs);
+ },
+
+ // Return the first model with matching attributes. Useful for simple cases
+ // of `find`.
+ findWhere: function(attrs) {
+ return this.where(attrs, true);
+ },
+ // Force the collection to re-sort itself. You don't need to call this under
+ // normal circumstances, as the set will maintain sort order as each item
+ // is added.
+ sort: function(options) {
+ var comparator = this.comparator;
+ if (!comparator) throw new Error('Cannot sort a set without a comparator');
+ options || (options = {});
- /**
- * Import, if possible, classes inside main container
- * @private
- * */
- importClasses: function importClasses() {
- var clm = this.config.em.get('SelectorManager');
+ var length = comparator.length;
+ if (_.isFunction(comparator)) comparator = _.bind(comparator, this);
- if (clm) {
- this.model.get('classes').each(function (m) {
- clm.add(m.get('name'));
- });
- }
- },
+ // Run sort based on type of `comparator`.
+ if (length === 1 || _.isString(comparator)) {
+ this.models = this.sortBy(comparator);
+ } else {
+ this.models.sort(comparator);
+ }
+ if (!options.silent) this.trigger('sort', this, options);
+ return this;
+ },
+ // Pluck an attribute from each model in the collection.
+ pluck: function(attr) {
+ return this.map(attr + '');
+ },
- /**
- * Fires on state update. If the state is not empty will add a helper class
- * @param {Event} e
- * @private
- * */
- updateState: function updateState(e) {
- var cl = 'hc-state';
- var state = this.model.get('state');
+ // Fetch the default set of models for this collection, resetting the
+ // collection when they arrive. If `reset: true` is passed, the response
+ // data will be passed through the `reset` method instead of `set`.
+ fetch: function(options) {
+ options = _.extend({parse: true}, options);
+ var success = options.success;
+ var collection = this;
+ options.success = function(resp) {
+ var method = options.reset ? 'reset' : 'set';
+ collection[method](resp, options);
+ if (success) success.call(options.context, collection, resp, options);
+ collection.trigger('sync', collection, resp, options);
+ };
+ wrapError(this, options);
+ return this.sync('read', this, options);
+ },
- if (state) {
- this.$el.addClass(cl);
- } else {
- this.$el.removeClass(cl);
- }
- },
+ // Create a new instance of a model in this collection. Add the model to the
+ // collection immediately, unless `wait: true` is passed, in which case we
+ // wait for the server to agree.
+ create: function(model, options) {
+ options = options ? _.clone(options) : {};
+ var wait = options.wait;
+ model = this._prepareModel(model, options);
+ if (!model) return false;
+ if (!wait) this.add(model, options);
+ var collection = this;
+ var success = options.success;
+ options.success = function(m, resp, callbackOpts) {
+ if (wait) collection.add(m, callbackOpts);
+ if (success) success.call(callbackOpts.context, m, resp, callbackOpts);
+ };
+ model.save(null, options);
+ return model;
+ },
+ // **parse** converts a response into a list of models to be added to the
+ // collection. The default implementation is just to pass it through.
+ parse: function(resp, options) {
+ return resp;
+ },
- /**
- * Update item on status change
- * @param {Event} e
- * @private
- * */
- updateStatus: function updateStatus() {
- var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ // Create a new collection with an identical list of models as this one.
+ clone: function() {
+ return new this.constructor(this.models, {
+ model: this.model,
+ comparator: this.comparator
+ });
+ },
- var em = this.em;
- var el = this.el;
- var status = this.model.get('status');
- var pfx = this.pfx;
- var ppfx = this.ppfx;
- var selectedCls = pfx + 'selected';
- var selectedParentCls = selectedCls + '-parent';
- var freezedCls = ppfx + 'freezed';
- var hoveredCls = ppfx + 'hovered';
- var toRemove = [selectedCls, selectedParentCls, freezedCls, hoveredCls];
- this.$el.removeClass(toRemove.join(' '));
- var actualCls = el.getAttribute('class') || '';
- var cls = '';
+ // Define how to uniquely identify models in the collection.
+ modelId: function(attrs) {
+ return attrs[this.model.prototype.idAttribute || 'id'];
+ },
- switch (status) {
- case 'selected':
- cls = actualCls + ' ' + selectedCls;
- break;
- case 'selected-parent':
- cls = actualCls + ' ' + selectedParentCls;
- break;
- case 'freezed':
- cls = actualCls + ' ' + freezedCls;
- break;
- case 'freezed-selected':
- cls = actualCls + ' ' + freezedCls + ' ' + selectedCls;
- break;
- case 'hovered':
- cls = !opts.avoidHover ? actualCls + ' ' + hoveredCls : '';
- break;
- }
+ // Private method to reset all internal state. Called when the collection
+ // is first initialized or reset.
+ _reset: function() {
+ this.length = 0;
+ this.models = [];
+ this._byId = {};
+ },
- cls = cls.trim();
- cls && el.setAttribute('class', cls);
- },
+ // Prepare a hash of attributes (or other model) to be added to this
+ // collection.
+ _prepareModel: function(attrs, options) {
+ if (this._isModel(attrs)) {
+ if (!attrs.collection) attrs.collection = this;
+ return attrs;
+ }
+ options = options ? _.clone(options) : {};
+ options.collection = this;
+ var model = new this.model(attrs, options);
+ if (!model.validationError) return model;
+ this.trigger('invalid', this, model.validationError, options);
+ return false;
+ },
+ // Internal method called by both remove and set.
+ _removeModels: function(models, options) {
+ var removed = [];
+ for (var i = 0; i < models.length; i++) {
+ var model = this.get(models[i]);
+ if (!model) continue;
- /**
- * Update highlight attribute
- * @private
- * */
- updateHighlight: function updateHighlight() {
- var hl = this.model.get('highlightable');
- this.setAttribute('data-highlightable', hl ? 1 : '');
- },
+ var index = this.indexOf(model);
+ this.models.splice(index, 1);
+ this.length--;
+ // Remove references before triggering 'remove' event to prevent an
+ // infinite loop. #3693
+ delete this._byId[model.cid];
+ var id = this.modelId(model.attributes);
+ if (id != null) delete this._byId[id];
- /**
- * Update style attribute
- * @private
- * */
- updateStyle: function updateStyle() {
- var em = this.em;
- var model = this.model;
+ if (!options.silent) {
+ options.index = index;
+ model.trigger('remove', model, this, options);
+ }
- if (em && em.get('avoidInlineStyle')) {
- this.el.id = model.getId();
- var style = model.getStyle();
- !(0, _underscore.isEmpty)(style) && model.setStyle(style);
- } else {
- this.setAttribute('style', model.styleToString());
- }
- },
+ removed.push(model);
+ this._removeReference(model, options);
+ }
+ return removed;
+ },
+ // Method for checking whether an object should be considered a model for
+ // the purposes of adding to the collection.
+ _isModel: function(model) {
+ return model instanceof Model;
+ },
- /**
- * Update classe attribute
- * @private
- * */
- updateClasses: function updateClasses() {
- var str = this.model.get('classes').pluck('name').join(' ');
- this.setAttribute('class', str);
+ // Internal method to create a model's ties to a collection.
+ _addReference: function(model, options) {
+ this._byId[model.cid] = model;
+ var id = this.modelId(model.attributes);
+ if (id != null) this._byId[id] = model;
+ model.on('all', this._onModelEvent, this);
+ },
- // Regenerate status class
- this.updateStatus();
- },
+ // Internal method to sever a model's ties to a collection.
+ _removeReference: function(model, options) {
+ delete this._byId[model.cid];
+ var id = this.modelId(model.attributes);
+ if (id != null) delete this._byId[id];
+ if (this === model.collection) delete model.collection;
+ model.off('all', this._onModelEvent, this);
+ },
+ // Internal method called every time a model in the set fires an event.
+ // Sets need to update their indexes when models change ids. All other
+ // events simply proxy through. "add" and "remove" events that originate
+ // in other collections are ignored.
+ _onModelEvent: function(event, model, collection, options) {
+ if (model) {
+ if ((event === 'add' || event === 'remove') && collection !== this) return;
+ if (event === 'destroy') this.remove(model, options);
+ if (event === 'change') {
+ var prevId = this.modelId(model.previousAttributes());
+ var id = this.modelId(model.attributes);
+ if (prevId !== id) {
+ if (prevId != null) delete this._byId[prevId];
+ if (id != null) this._byId[id] = model;
+ }
+ }
+ }
+ this.trigger.apply(this, arguments);
+ }
- /**
- * Update single attribute
- * @param {[type]} name [description]
- * @param {[type]} value [description]
- */
- setAttribute: function setAttribute(name, value) {
- var el = this.$el;
- value ? el.attr(name, value) : el.removeAttr(name);
- },
+ });
+ // Underscore methods that we want to implement on the Collection.
+ // 90% of the core usefulness of Backbone Collections is actually implemented
+ // right here:
+ var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0,
+ foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3,
+ select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
+ contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
+ head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
+ without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
+ isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
+ sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
- /**
- * Get classes from attributes.
- * This method is called before initialize
- *
- * @return {Array}|null
- * @private
- * */
- getClasses: function getClasses() {
- var attr = this.model.get('attributes'),
- classes = attr['class'] || [];
- classes = (0, _underscore.isArray)(classes) ? classes : [classes];
+ // Mix in each Underscore method as a proxy to `Collection#models`.
+ addUnderscoreMethods(Collection, collectionMethods, 'models');
- if (classes.length) {
- return classes.join(' ');
- } else {
- return null;
- }
- },
+ // Backbone.View
+ // -------------
+ // Backbone Views are almost more convention than they are actual code. A View
+ // is simply a JavaScript object that represents a logical chunk of UI in the
+ // DOM. This might be a single item, an entire list, a sidebar or panel, or
+ // even the surrounding frame which wraps your whole app. Defining a chunk of
+ // UI as a **View** allows you to define your DOM events declaratively, without
+ // having to worry about render order ... and makes it easy for the view to
+ // react to specific changes in the state of your models.
- /**
- * Update attributes
- * @private
- * */
- updateAttributes: function updateAttributes() {
- var model = this.model;
- var attrs = { 'data-gjs-type': model.get('type') || 'default' };
- var attr = model.get('attributes');
- var src = model.get('src');
+ // Creating a Backbone.View creates its initial element outside of the DOM,
+ // if an existing element is not provided...
+ var View = Backbone.View = function(options) {
+ this.cid = _.uniqueId('view');
+ _.extend(this, _.pick(options, viewOptions));
+ this._ensureElement();
+ this.initialize.apply(this, arguments);
+ };
- for (var key in attr) {
- attrs[key] = attr[key];
- }
+ // Cached regex to split keys for `delegate`.
+ var delegateEventSplitter = /^(\S+)\s*(.*)$/;
- src && (attrs.src = src);
- this.$el.attr(attrs);
- this.updateHighlight();
- this.updateStyle();
- },
+ // List of view options to be set as properties.
+ var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
+ // Set up all inheritable **Backbone.View** properties and methods.
+ _.extend(View.prototype, Events, {
- /**
- * Update component content
- * @private
- * */
- updateContent: function updateContent() {
- this.getChildrenContainer().innerHTML = this.model.get('content');
- },
+ // The default `tagName` of a View's element is `"div"`.
+ tagName: 'div',
+ // jQuery delegate for element lookup, scoped to DOM elements within the
+ // current view. This should be preferred to global lookups where possible.
+ $: function(selector) {
+ return this.$el.find(selector);
+ },
- /**
- * Prevent default helper
- * @param {Event} e
- * @private
- */
- prevDef: function prevDef(e) {
- e.preventDefault();
- },
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize: function(){},
+ // **render** is the core function that your view should override, in order
+ // to populate its element (`this.el`), with the appropriate HTML. The
+ // convention is for **render** to always return `this`.
+ render: function() {
+ return this;
+ },
- /**
- * Render component's script
- * @private
- */
- updateScript: function updateScript() {
- if (!this.model.get('script')) {
- return;
- }
+ // Remove this view by taking the element out of the DOM, and removing any
+ // applicable Backbone.Events listeners.
+ remove: function() {
+ this._removeElement();
+ this.stopListening();
+ return this;
+ },
- var em = this.em;
- if (em) {
- var canvas = em.get('Canvas');
- canvas.getCanvasView().updateScript(this);
- }
- },
+ // Remove this view's element from the document and all event listeners
+ // attached to it. Exposed for subclasses using an alternative DOM
+ // manipulation API.
+ _removeElement: function() {
+ this.$el.remove();
+ },
+ // Change the view's element (`this.el` property) and re-delegate the
+ // view's events on the new element.
+ setElement: function(element) {
+ this.undelegateEvents();
+ this._setElement(element);
+ this.delegateEvents();
+ return this;
+ },
- /**
- * Return children container
- * Differently from a simple component where children container is the
- * component itself
- *
,