diff --git a/css/bootstrap/bootstrap-tooltip-custom.css b/css/bootstrap/bootstrap-tooltip-custom.css new file mode 100644 index 00000000..8b1add44 --- /dev/null +++ b/css/bootstrap/bootstrap-tooltip-custom.css @@ -0,0 +1,8 @@ +.tooltip.in { + opacity: 0.95; + filter: alpha(opacity=95); +} + +.tooltip { + line-height: 20px; +} \ No newline at end of file diff --git a/css/bootstrap/bootstrap.css b/css/bootstrap/bootstrap.css new file mode 100755 index 00000000..724ca00b --- /dev/null +++ b/css/bootstrap/bootstrap.css @@ -0,0 +1,112 @@ +/*! + * Bootstrap v2.1.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ +.clearfix { + *zoom: 1; +} +.clearfix:before, +.clearfix:after { + display: table; + content: ""; + line-height: 0; +} +.clearfix:after { + clear: both; +} +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.input-block-level { + display: block; + width: 100%; + min-height: 30px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.tooltip { + position: absolute; + z-index: 1030; + display: block; + visibility: visible; + padding: 5px; + font-size: 11px; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.tooltip.top { + margin-top: -3px; +} +.tooltip.right { + margin-left: 3px; +} +.tooltip.bottom { + margin-top: 3px; +} +.tooltip.left { + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} + +.tooltip { + line-height: 20px; +} diff --git a/css/bootstrap/readme.txt b/css/bootstrap/readme.txt new file mode 100644 index 00000000..2a24c100 --- /dev/null +++ b/css/bootstrap/readme.txt @@ -0,0 +1 @@ +Bootstrap custom build containing only the tooltip component \ No newline at end of file diff --git a/css/preview-animation-section.css b/css/preview-animation-section.css new file mode 100644 index 00000000..d970b9c0 --- /dev/null +++ b/css/preview-animation-section.css @@ -0,0 +1,20 @@ + +.preview-container { + position : absolute; + bottom : 0; right : 0; + height : 256px; + width : 256px; + background : white; + border : 0px Solid black; + border-radius:5px 0px 0px 5px; + box-shadow : 0px 0px 2px rgba(0,0,0,0.2); +} + +.preview-container canvas { + border : 0px Solid transparent; + border-radius:5px 0px 0px 5px; +} + +#preview-fps { + width : 200px; +} \ No newline at end of file diff --git a/css/preview-film-section.css b/css/preview-film-section.css index 0c9a397b..f6836592 100644 --- a/css/preview-film-section.css +++ b/css/preview-film-section.css @@ -1,12 +1,32 @@ +.preview-list-wrapper { + overflow-y: scroll; + position: absolute; + top: 30px; + right:0; + bottom: 0; + left: 0; +} + .preview-list { list-style-type: none; + padding-left: 7px; + padding-right: 7px; } .preview-tile { padding : 10px; overflow: hidden; background-color: gray; + border-radius: 2px; + + -webkit-box-shadow: 0 0 7px 0 rgba(0, 0, 0, 1); + box-shadow: 0 0 7px 0 rgba(0, 0, 0, 1); +} + +.preview-tile:hover { + -webkit-box-shadow: 0 0 10px 0 rgba(0, 0, 0, 1); + box-shadow: 0 0 10px 0 rgba(0, 0, 0, 1); } .preview-tile .canvas-container { @@ -15,18 +35,33 @@ .preview-tile .tile-view { float: left; - border: blue 1px solid; + border: #ccc 1px solid; } .preview-tile .tile-action { display: none; float: right; + cursor: pointer; + width: 30px; + height: 30px; + background-color: transparent; + background-repeat: no-repeat; + background-position: 7px 7px; + border: none; } .preview-tile:hover .tile-action { display: block; } +.preview-tile .tile-action.duplicate-frame-action { + background-image: url(../img/actions/duplicate.png); +} + +.preview-tile .tile-action.delete-frame-action { + background-image: url(../img/actions/delete.png); +} + .preview-tile:hover { background-color: lightgray; } diff --git a/css/reset.css b/css/reset.css index 19738719..30a499a0 100644 --- a/css/reset.css +++ b/css/reset.css @@ -17,4 +17,17 @@ ul, li { margin : 0; padding : 0; list-style-type: none; +} + + +/* Force apparition of scrollbars on leopard */ +::-webkit-scrollbar { + -webkit-appearance: none; + width: 10px; +} + +::-webkit-scrollbar-thumb { + border-radius: 4px; + background-color: rgba(180,180,180,.7); + -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5); } \ No newline at end of file diff --git a/css/style.css b/css/style.css index cd278e6a..84f908f7 100644 --- a/css/style.css +++ b/css/style.css @@ -1,58 +1,59 @@ + + +/** + * Top menubars + */ .menubar { - position: absolute; - top: 0; left: 0; - width: 100%; height: 30px; + background-color: #fff; + width: 100%; + height: 30px; } -.left-nav { - position:absolute; - top : 30px; bottom : 0; - width : 200px; - overflow-y: scroll; - background : #000; - padding : 10px; + +/** + * Application layout + */ + +.left-section { + position: absolute; + top: 0; + bottom: 0; + width: 220px; + background: #666; } .main-panel { - position:absolute; - top : 30px; bottom : 0; left : 220px; right : 0; - background : #ccc; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 220px; + background: #ccc; } -.preview-container { - position : absolute; - bottom : 0; right : 0; - height : 256px; - width : 256px; - background : white; - border : 0px Solid black; - border-radius:5px 0px 0px 5px; - box-shadow : 0px 0px 2px rgba(0,0,0,0.2); + +/** + * CSS Helpers + */ + +.overlay-total { + position: absolute; + top: 0; + right:0; + bottom: 0; + left: 0; } -.preview-container canvas { - border : 0px Solid transparent; - border-radius:5px 0px 0px 5px; -} -#cursorInfo { - position : fixed; - cursor : default; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} +/** + * Canvases layout + */ -.action-button { - background-color : white; - width : 150px; - display : inline-block; +.canvas { + position: relative; + z-index: 1; } - .canvas-container { position: relative; display: block; @@ -67,42 +68,35 @@ left: 0; } -.canvas { - position: relative; - z-index: 1; -} - -.drawing-canvas { - float: left; -} - -.canvas-overlay { +.canvas.canvas-overlay { position: absolute; top: 0; left: 0; z-index: 10; } +.drawing-canvas { + float: left; +} + .drawing-canvas-container { float: left; } -#preview-fps { - width : 200px; -} + /** * User messages */ .user-message { position: absolute; - top: 2px; - right: 0; + bottom: 0; + left: 25%; background-color: #F9EDBE; padding: 4px 12px; padding-right: 20px; border-top-left-radius: 7px; - border-bottom-left-radius: 7px; + border-top-right-radius: 7px; border-right: 0; color: #222; border: #F0C36D 1px solid; @@ -124,15 +118,3 @@ .user-message .close:hover { color: black; } - -/* Force apparition of scrollbars on leopard */ -::-webkit-scrollbar { - -webkit-appearance: none; - width: 7px; -} - -::-webkit-scrollbar-thumb { - border-radius: 4px; - background-color: rgba(180,180,180,.7); - -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5); -} \ No newline at end of file diff --git a/css/tools.css b/css/tools.css index 48abd183..b31b333e 100644 --- a/css/tools.css +++ b/css/tools.css @@ -1,7 +1,7 @@ .tools-group { float: left; height: 30px; - border-right: 1px solid #ccc; + border-left: 1px solid #ccc; padding: 0 3px; } @@ -19,11 +19,11 @@ * Framesheet level actions: */ .tool-icon.tool-save { - background-image: url(../img/tools/icons/save.png); + background-image: url(../img/actions/save.png); } .tool-icon.tool-add-frame { - background-image: url(../img/tools/icons/add-frame.png); + background-image: url(../img/actions/add.png); } @@ -31,47 +31,47 @@ * Tool icons: */ .tool-icon.tool-pen { - background-image: url(../img/tools/icons/pen.png); + background-image: url(../img/tools/pen.png); } .tool-icon.tool-vertical-mirror-pen { - background-image: url(../img/tools/icons/vertical-mirror-pen.png); + background-image: url(../img/tools/vertical-mirror-pen.png); } .tool-icon.tool-paint-bucket { - background-image: url(../img/tools/icons/paint-bucket.png); + background-image: url(../img/tools/paint-bucket.png); } .tool-icon.tool-eraser { - background-image: url(../img/tools/icons/eraser.png); + background-image: url(../img/tools/eraser.png); } .tool-icon.tool-stroke { - background-image: url(../img/tools/icons/stroke.png); + background-image: url(../img/tools/stroke.png); } .tool-icon.tool-rectangle { - background-image: url(../img/tools/icons/rectangle.png); + background-image: url(../img/tools/rectangle.png); } .tool-icon.tool-circle { - background-image: url(../img/tools/cursors/circle.png); + background-image: url(../img/tools/circle.png); } .tool-icon.tool-move { - background-image: url(../img/tools/icons/hand.png); + background-image: url(../img/tools/hand.png); } .tool-icon.tool-rectangle-select { - background-image: url(../img/tools/icons/select.png); + background-image: url(../img/tools/select.png); } .tool-icon.tool-shape-select { - background-image: url(../img/tools/icons/wand.png); + background-image: url(../img/tools/wand.png); } /*.tool-icon.tool-palette { - background-image: url(../img/tools/icons/color-palette.png); + background-image: url(../img/tools/color-palette.png); }*/ /* @@ -79,43 +79,43 @@ */ .tool-paint-bucket .drawing-canvas-container:hover { - cursor: url(../img/tools/cursors/paint-bucket.png) 14 12, pointer; + cursor: url(../img/tools/paint-bucket.png) 14 12, pointer; } .tool-vertical-mirror-pen .drawing-canvas-container:hover { - cursor: url(../img/tools/cursors/vertical-mirror-pen.png) 14 12, pointer; + cursor: url(../img/tools/vertical-mirror-pen.png) 14 12, pointer; } .tool-pen .drawing-canvas-container:hover { - cursor: url(../img/tools/cursors/pen.png) 2 21, pointer; + cursor: url(../img/tools/pen.png) 2 21, pointer; } .tool-eraser .drawing-canvas-container:hover { - cursor: url(../img/tools/cursors/eraser.png) 2 21, pointer; + cursor: url(../img/tools/eraser.png) 2 21, pointer; } .tool-stroke .drawing-canvas-container:hover { - cursor: url(../img/tools/cursors/pen.png) 2 21, pointer; + cursor: url(../img/tools/pen.png) 2 21, pointer; } .tool-rectangle .drawing-canvas-container:hover { - cursor: url(../img/tools/cursors/rectangle.png) 4 21, pointer; + cursor: url(../img/tools/rectangle.png) 4 21, pointer; } .tool-circle .drawing-canvas-container:hover { - cursor: url(../img/tools/cursors/circle.png) 4 21, pointer; + cursor: url(../img/tools/circle.png) 4 21, pointer; } .tool-move .drawing-canvas-container:hover { - cursor: url(../img/tools/cursors/hand.png) 14 12, pointer; + cursor: url(../img/tools/hand.png) 14 12, pointer; } .tool-rectangle-select .drawing-canvas-container:hover { - cursor: url(../img/tools/cursors/select.png) 14 12, pointer; + cursor: url(../img/tools/select.png) 14 12, pointer; } .tool-shape-select .drawing-canvas-container:hover { - cursor: url(../img/tools/cursors/wand.png) 14 12, pointer; + cursor: url(../img/tools/wand.png) 14 12, pointer; } .tool-grid, @@ -136,6 +136,7 @@ .tool-color-picker { padding: 5px 0 0 5px; + height: 25px; cursor : default; } @@ -148,7 +149,7 @@ position : relative; } -#secondary-color-picker { +.secondary-color-picker { top : 8px; } diff --git a/img/tools/icons/add-frame.png b/img/actions/add.png similarity index 100% rename from img/tools/icons/add-frame.png rename to img/actions/add.png diff --git a/img/actions/delete.png b/img/actions/delete.png new file mode 100755 index 00000000..08f24936 Binary files /dev/null and b/img/actions/delete.png differ diff --git a/img/actions/duplicate.png b/img/actions/duplicate.png new file mode 100755 index 00000000..195dc6d6 Binary files /dev/null and b/img/actions/duplicate.png differ diff --git a/img/tools/icons/save.png b/img/actions/save.png similarity index 100% rename from img/tools/icons/save.png rename to img/actions/save.png diff --git a/img/tools/circle.png b/img/tools/circle.png new file mode 100644 index 00000000..a186a26b Binary files /dev/null and b/img/tools/circle.png differ diff --git a/img/tools/color-palette.png b/img/tools/color-palette.png new file mode 100755 index 00000000..6e6e8521 Binary files /dev/null and b/img/tools/color-palette.png differ diff --git a/img/tools/eraser.png b/img/tools/eraser.png new file mode 100755 index 00000000..e0d4b020 Binary files /dev/null and b/img/tools/eraser.png differ diff --git a/img/tools/hand.png b/img/tools/hand.png new file mode 100644 index 00000000..7b47be2d Binary files /dev/null and b/img/tools/hand.png differ diff --git a/img/tools/mirror-pen.png b/img/tools/mirror-pen.png new file mode 100644 index 00000000..f5148946 Binary files /dev/null and b/img/tools/mirror-pen.png differ diff --git a/img/tools/paint-bucket.png b/img/tools/paint-bucket.png new file mode 100755 index 00000000..f82a8865 Binary files /dev/null and b/img/tools/paint-bucket.png differ diff --git a/img/tools/pen.png b/img/tools/pen.png new file mode 100755 index 00000000..0bfecd50 Binary files /dev/null and b/img/tools/pen.png differ diff --git a/img/tools/rectangle.png b/img/tools/rectangle.png new file mode 100755 index 00000000..d28dc6b1 Binary files /dev/null and b/img/tools/rectangle.png differ diff --git a/img/tools/select.png b/img/tools/select.png new file mode 100644 index 00000000..bf2b724d Binary files /dev/null and b/img/tools/select.png differ diff --git a/img/tools/stroke.png b/img/tools/stroke.png new file mode 100755 index 00000000..0bfecd50 Binary files /dev/null and b/img/tools/stroke.png differ diff --git a/img/tools/vertical-mirror-pen.png b/img/tools/vertical-mirror-pen.png new file mode 100644 index 00000000..cf2157d8 Binary files /dev/null and b/img/tools/vertical-mirror-pen.png differ diff --git a/img/tools/wand.png b/img/tools/wand.png new file mode 100755 index 00000000..44ccbf81 Binary files /dev/null and b/img/tools/wand.png differ diff --git a/index.html b/index.html index 0e0f466a..e6979f6f 100644 --- a/index.html +++ b/index.html @@ -12,57 +12,51 @@ + + - + - - - -
+
+ - +
+
    +
    + +
    @@ -70,17 +64,19 @@
    + + 12 FPS
    -
    - + + diff --git a/js/Palette.js b/js/Palette.js index 8460e398..59bc8934 100644 --- a/js/Palette.js +++ b/js/Palette.js @@ -24,7 +24,7 @@ pskl.Palette = (function() { */ var createPalette_ = function (colors) { // Always adding transparent color - paletteRoot.html(''); + paletteRoot.html(''); for(var color in colors) { if(color != Constants.TRANSPARENT_COLOR) { addColorToPalette_(color); @@ -39,6 +39,8 @@ pskl.Palette = (function() { if (paletteColors.indexOf(color) == -1 && color != Constants.TRANSPARENT_COLOR) { var colorEl = document.createElement("li"); colorEl.className = "palette-color"; + colorEl.setAttribute("rel", "tooltip"); + colorEl.setAttribute("data-placement", "bottom"); colorEl.setAttribute("data-color", color); colorEl.setAttribute("title", color); colorEl.style.background = color; diff --git a/js/controller/AnimatedPreviewController.js b/js/controller/AnimatedPreviewController.js index 34b84eef..76a6464e 100644 --- a/js/controller/AnimatedPreviewController.js +++ b/js/controller/AnimatedPreviewController.js @@ -20,7 +20,8 @@ }; ns.AnimatedPreviewController.prototype.onFPSSliderChange = function(evt) { - this.fps = parseInt($("#preview-fps")[0].value, 10); + this.fps = parseInt($("#preview-fps")[0].value, 10); + $("#display-fps").html(this.fps + " FPS") }; ns.AnimatedPreviewController.prototype.render = function (delta) { diff --git a/js/controller/PreviewFilmController.js b/js/controller/PreviewFilmController.js index 025257eb..626ad8fd 100644 --- a/js/controller/PreviewFilmController.js +++ b/js/controller/PreviewFilmController.js @@ -35,7 +35,10 @@ }; ns.PreviewFilmController.prototype.createPreviews_ = function () { - this.container.html(""); + + this.container.html(""); + // Manually remove tooltips since mouseout events were shortcut by the DOM refresh: + $(".tooltip").remove(); var frameCount = this.framesheet.getFrameCount(); @@ -174,9 +177,11 @@ previewTileRoot.addEventListener('click', this.onPreviewClick_.bind(this, tileNumber)); var canvasPreviewDuplicateAction = document.createElement("button"); - canvasPreviewDuplicateAction.className = "tile-action" - canvasPreviewDuplicateAction.innerHTML = "dup" - + canvasPreviewDuplicateAction.setAttribute('rel', 'tooltip'); + canvasPreviewDuplicateAction.setAttribute('data-placement', 'right'); + canvasPreviewDuplicateAction.setAttribute('title', 'Duplicate this frame'); + canvasPreviewDuplicateAction.className = "tile-action duplicate-frame-action" + canvasPreviewDuplicateAction.addEventListener('click', this.onAddButtonClick_.bind(this, tileNumber)); // TODO(vincz): Eventually optimize this part by not recreating a FrameRenderer. Note that the real optim @@ -190,8 +195,10 @@ if(tileNumber > 0 || this.framesheet.getFrameCount() > 1) { var canvasPreviewDeleteAction = document.createElement("button"); - canvasPreviewDeleteAction.className = "tile-action" - canvasPreviewDeleteAction.innerHTML = "del" + canvasPreviewDeleteAction.setAttribute('rel', 'tooltip'); + canvasPreviewDeleteAction.setAttribute('data-placement', 'right'); + canvasPreviewDeleteAction.setAttribute('title', 'Delete this frame'); + canvasPreviewDeleteAction.className = "tile-action delete-frame-action" canvasPreviewDeleteAction.addEventListener('click', this.onDeleteButtonClick_.bind(this, tileNumber)); previewTileRoot.appendChild(canvasPreviewDeleteAction); } diff --git a/js/controller/ToolController.js b/js/controller/ToolController.js index 6553cbb6..5fe0f50c 100644 --- a/js/controller/ToolController.js +++ b/js/controller/ToolController.js @@ -72,7 +72,7 @@ // TODO(vincz): Tools rendering order is not enforced by the data stucture (this.toolInstances), fix that. for (var toolKey in this.toolInstances) { currentTool = this.toolInstances[toolKey]; - toolMarkup += '
  • '; } $('#tools-container').html(toolMarkup); diff --git a/js/drawingtools/SimplePen.js b/js/drawingtools/SimplePen.js index 60490f3a..dcbd89fa 100644 --- a/js/drawingtools/SimplePen.js +++ b/js/drawingtools/SimplePen.js @@ -22,10 +22,10 @@ */ ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay) { if (frame.containsPixel(col, row)) { - this.previousCol = col; - this.previousRow = row; - frame.setPixel(col, row, color); + frame.setPixel(col, row, color); } + this.previousCol = col; + this.previousRow = row; }; ns.SimplePen.prototype.moveToolAt = function(col, row, color, frame, overlay) { diff --git a/js/lib/bootstrap/bootstrap.js b/js/lib/bootstrap/bootstrap.js new file mode 100755 index 00000000..ed628853 --- /dev/null +++ b/js/lib/bootstrap/bootstrap.js @@ -0,0 +1,275 @@ +/* =========================================================== + * bootstrap-tooltip.js v2.1.1 + * http://twitter.github.com/bootstrap/javascript.html#tooltips + * Inspired by the original jQuery.tipsy by Jason Frame + * =========================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function ($) { + + "use strict"; // jshint ;_; + + + /* TOOLTIP PUBLIC CLASS DEFINITION + * =============================== */ + + var Tooltip = function (element, options) { + this.init('tooltip', element, options) + } + + Tooltip.prototype = { + + constructor: Tooltip + + , init: function (type, element, options) { + var eventIn + , eventOut + + this.type = type + this.$element = $(element) + this.options = this.getOptions(options) + this.enabled = true + + if (this.options.trigger == 'click') { + this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) + } else if (this.options.trigger != 'manual') { + eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus' + eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur' + this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) + this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) + } + + this.options.selector ? + (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : + this.fixTitle() + } + + , getOptions: function (options) { + options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data()) + + if (options.delay && typeof options.delay == 'number') { + options.delay = { + show: options.delay + , hide: options.delay + } + } + + return options + } + + , enter: function (e) { + var self = $(e.currentTarget)[this.type](this._options).data(this.type) + + if (!self.options.delay || !self.options.delay.show) return self.show() + + clearTimeout(this.timeout) + self.hoverState = 'in' + this.timeout = setTimeout(function() { + if (self.hoverState == 'in') self.show() + }, self.options.delay.show) + } + + , leave: function (e) { + var self = $(e.currentTarget)[this.type](this._options).data(this.type) + + if (this.timeout) clearTimeout(this.timeout) + if (!self.options.delay || !self.options.delay.hide) return self.hide() + + self.hoverState = 'out' + this.timeout = setTimeout(function() { + if (self.hoverState == 'out') self.hide() + }, self.options.delay.hide) + } + + , show: function () { + var $tip + , inside + , pos + , actualWidth + , actualHeight + , placement + , tp + + if (this.hasContent() && this.enabled) { + $tip = this.tip() + this.setContent() + + if (this.options.animation) { + $tip.addClass('fade') + } + + placement = typeof this.options.placement == 'function' ? + this.options.placement.call(this, $tip[0], this.$element[0]) : + this.options.placement + + inside = /in/.test(placement) + + $tip + .remove() + .css({ top: 0, left: 0, display: 'block' }) + .appendTo(inside ? this.$element : document.body) + + pos = this.getPosition(inside) + + actualWidth = $tip[0].offsetWidth + actualHeight = $tip[0].offsetHeight + + switch (inside ? placement.split(' ')[1] : placement) { + case 'bottom': + tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2} + break + case 'top': + tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2} + break + case 'left': + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth} + break + case 'right': + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width} + break + } + + $tip + .css(tp) + .addClass(placement) + .addClass('in') + } + } + + , setContent: function () { + var $tip = this.tip() + , title = this.getTitle() + + $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) + $tip.removeClass('fade in top bottom left right') + } + + , hide: function () { + var that = this + , $tip = this.tip() + + $tip.removeClass('in') + + function removeWithAnimation() { + var timeout = setTimeout(function () { + $tip.off($.support.transition.end).remove() + }, 500) + + $tip.one($.support.transition.end, function () { + clearTimeout(timeout) + $tip.remove() + }) + } + + $.support.transition && this.$tip.hasClass('fade') ? + removeWithAnimation() : + $tip.remove() + + return this + } + + , fixTitle: function () { + var $e = this.$element + if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') { + $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title') + } + } + + , hasContent: function () { + return this.getTitle() + } + + , getPosition: function (inside) { + return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), { + width: this.$element[0].offsetWidth + , height: this.$element[0].offsetHeight + }) + } + + , getTitle: function () { + var title + , $e = this.$element + , o = this.options + + title = $e.attr('data-original-title') + || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) + + return title + } + + , tip: function () { + return this.$tip = this.$tip || $(this.options.template) + } + + , validate: function () { + if (!this.$element[0].parentNode) { + this.hide() + this.$element = null + this.options = null + } + } + + , enable: function () { + this.enabled = true + } + + , disable: function () { + this.enabled = false + } + + , toggleEnabled: function () { + this.enabled = !this.enabled + } + + , toggle: function () { + this[this.tip().hasClass('in') ? 'hide' : 'show']() + } + + , destroy: function () { + this.hide().$element.off('.' + this.type).removeData(this.type) + } + + } + + + /* TOOLTIP PLUGIN DEFINITION + * ========================= */ + + $.fn.tooltip = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('tooltip') + , options = typeof option == 'object' && option + if (!data) $this.data('tooltip', (data = new Tooltip(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.tooltip.Constructor = Tooltip + + $.fn.tooltip.defaults = { + animation: true + , placement: 'top' + , selector: false + , template: '
    ' + , trigger: 'hover' + , title: '' + , delay: 0 + , html: true + } + +}(window.jQuery); diff --git a/js/lib/bootstrap/readme.txt b/js/lib/bootstrap/readme.txt new file mode 100644 index 00000000..2a24c100 --- /dev/null +++ b/js/lib/bootstrap/readme.txt @@ -0,0 +1 @@ +Bootstrap custom build containing only the tooltip component \ No newline at end of file diff --git a/js/piskel.js b/js/piskel.js index 06fa6449..c7b760f0 100644 --- a/js/piskel.js +++ b/js/piskel.js @@ -88,6 +88,11 @@ $.namespace("pskl"); drawingLoop.addCallback(this.render, this); drawingLoop.start(); + // Init (event-delegated) bootstrap tooltips: + $('body').tooltip({ + selector: '[rel=tooltip]' + }); + this.connectResizeToDpiUpdate_(); }, @@ -115,7 +120,10 @@ $.namespace("pskl"); * @private */ calculateDPIsForDrawingCanvas_ : function() { - var availableViewportHeight = $('.main-panel').height() - 50, + + var userMessageGap = 80; // Reserve some height to show the user message at the bottom + + var availableViewportHeight = $('.main-panel').height() - userMessageGap, availableViewportWidth = $('.main-panel').width(), previewHeight = $(".preview-container").height(), previewWidth = $(".preview-container").width();