/*global document, window, $, ko, debug, setTimeout, alert */ /* * Knockout UI Window * * Copyright (c) 2011 Ian Mckay * * https://github.com/madcapnmckay/Knockout-UI * * License: MIT http://www.opensource.org/licenses/mit-license.php * */ (function () { // Private function var logger = function (log) { if (typeof debug !== 'undefined') { $('
').appendTo('#log').text(new Date().toGMTString() + ' : eyepatch-window.js - ' + log); } }, templateEngine = new ko.jqueryTmplTemplateEngine(), Button = function (data, parentViewModel) { this.parentViewModel = parentViewModel; this.iconCssClass = ko.observable(data.iconCssClass); this.cssClass = ko.observable(data.cssClass || 'title-button'); this.onClick = data.onClick; this.title = ko.observable(data.title || ''); this.isFirst = ko.observable(data.isFirst); this.isLast = ko.observable(data.isLast); this.clicked = function (node) { if (this.onClick !== undefined) { if (this.onClick === 'minimize') { this.parentViewModel.minimize(); } else if (this.onClick === 'close') { this.parentViewModel.close(); } else if (typeof this.onClick == 'function') { this.onClick.call(this.parentViewModel, this); } else { // last ditch try and eval this.onClick = eval(data.onClick); this.onClick.call(this.parentViewModel, this); } } }.bind(this); }, Window = function (data, parentViewModel) { this.parentViewModel = parentViewModel; this.cssClass = ko.observable(data.cssClass || parentViewModel.cssClass()); this.id = data.id; this.name = ko.observable(data.name); this.width = ko.observable(data.width || 500); this.height = ko.observable(data.height || 500); this.isPinned = ko.observable(data.isPinned || false); this.contents = data.contents; this.create = typeof data.create == 'function' ? data.create : eval(data.create); this.taskbarCssClass = data.taskbarCssClass; this.buttons = ko.observableArray([]); var inputPosition = $.cookie(this.id + 'position') || data.position; this.position = ko.observable(inputPosition || '10,10'); var savedMinimized = $.cookie(this.id + 'state'); this.isLoading = ko.observable(false); this.isMinimized = ko.observable(false); if (savedMinimized !== null) { this.isMinimized(savedMinimized === 'true'); } else if (data.isMinimized !== undefined) { this.isMinimized(data.isMinimized); } this.buttonsChange = function() { for (var i = 0; i < data.buttons.length; i += 1) { data.buttons[i].isFirst = (i === 0); data.buttons[i].isLast = (i === data.buttons.length - 1); this.buttons.push(new Button(data.buttons[i], this)); } }.bind(this); this.buttonsChange(); this.buttons.subscribe(this.buttonsChange); this.left = function() { return this.position().split(',')[0] + 'px' }.bind(this); this.top = function() { return this.position().split(',')[1] + 'px' }.bind(this); this.minimize = function () { this.isMinimized(!this.isMinimized()); }.bind(this); this.close = function() { this.parentViewModel.removeWindow(this.id); } this.saveState = function () { if ($.cookie !== undefined) { $.cookie(this.id + 'position', this.position()); $.cookie(this.id + 'state', this.isMinimized()); } }.bind(this); }; ko.windowManager = { viewModel: function (configuration) { this.cssClass = ko.observable(configuration.cssClass || 'ui-window'); this.taskbarCssClass = ko.observable(configuration.taskbarCssClass || 'ui-taskbar'); this.windows = ko.observableArray([]); ko.utils.arrayForEach(configuration.windows, function(data) { this.windows.push(new Window(data, this)); }.bind(this)); this.addWindow = function(data) { this.windows.push(new Window(data, this)); }; this.removeWindow = function(id) { var window = this.windows.remove(function(item) { return item.id === id }); }; } }; ko.addTemplateSafe("windowTemplate", "\
\
\
\
\
\
\
\
\
", templateEngine); ko.addTemplateSafe("windowButtonTemplate", "\
", templateEngine); ko.addTemplateSafe("koTaskbarItemTemplate", "\
\
\
\
\
\
", templateEngine); ko.addTemplateSafe("windowContainerTemplate", "\
\
\
\
\
\
", templateEngine); ko.bindingHandlers.windowManager = { init: function (element, valueAccessor, allBindingsAccessor, viewModel) { var value = valueAccessor(); windowContainer = element.appendChild(document.createElement("DIV")); ko.renderTemplate("windowContainerTemplate", value, { templateEngine: templateEngine }, windowContainer, "replaceNode"); }, update : function (element, valueAccessor, allBindingsAccessor, viewModel) { var value = valueAccessor(), cssClass = value.cssClass(); // render the window bodies var windowContainers = $('.' + cssClass + ' .inner-content', element); for (var i = 0; i < value.windows().length; i += 1) { var windowVM = value.windows()[i]; if (windowVM.rendered) { continue; } windowVM.create(windowContainers[i], windowVM, viewModel); windowVM.rendered = true; } } }; ko.bindingHandlers.window = { init: function (element, valueAccessor, allBindingsAccessor, viewModel) { var $element = $(element), pinned = ko.utils.unwrapObservable(valueAccessor()), dragOptions = { addClasses: false, handle : '.title-bar', /*stack: ".ui-window-stack",*/ scroll : false, stop : function (e, ui) { viewModel.position(ui.offset.left + ',' + ui.offset.top); // save position and state in cookie viewModel.saveState(); $(this).maxZIndex({ group : '.ui-window-stack', inc : 1, min: 100000}); }, drag: function(event, ui) { $(this).maxZIndex({ group : '.ui-window-stack', inc : 1, min: 100000}); }, create: function(event, ui) { // set pinned status if (pinned) { $element.css('position', 'fixed'); } } }; // bind events $element.draggable(dragOptions).bind('mousedown', function () { $(this).maxZIndex({ group : '.ui-window-stack', inc : 1, min: 100000}); }); $element.css({'position' : 'absolute', 'top': viewModel.top(), 'left' : viewModel.left() }); }, update : function (element, valueAccessor, allBindingsAccessor, viewModel) { var $element = $(element), pinned = ko.utils.unwrapObservable(valueAccessor()); if (pinned) { $element.css('position','fixed'); } else { $element.css('position','absolute'); } } }; ko.bindingHandlers.taskbarVisible = { 'init': function (element, valueAccessor) { var $element = $(element), value = ko.utils.unwrapObservable(valueAccessor()), isCurrentlyVisible = element.style.display !== "none"; if (value && !isCurrentlyVisible) { element.style.display = ""; } else if ((!value) && isCurrentlyVisible) { element.style.display = "none"; } }, 'update': function (element, valueAccessor) { var $element = $(element), value = ko.utils.unwrapObservable(valueAccessor()), isCurrentlyVisible = element.style.display !== "none", width = $element.outerWidth(true), height = $element.outerHeight(true); if (value && !isCurrentlyVisible) { element.style.display = ""; } else if ((!value) && isCurrentlyVisible) { $element.fadeOut('slow', 'swing', function () { $('
').width(width).height(height).css('display', 'inline-block').insertAfter($element).animate({ width: 0}, 500, function () { $(this).remove(); }); }); } } }; ko.bindingHandlers.windowVisible = { 'init': function (element, valueAccessor, allBindingsAccessor, viewModel) { var $element = $(element), show = ko.utils.unwrapObservable(valueAccessor()); if (show) { $element.css({ 'top': viewModel.top(), 'left' : viewModel.left() }); } else { // instead of using display none which can mess up the contents of the window $element.css({ 'left' : (-viewModel.width() * 2) + 'px' }); } }, 'update': function (element, valueAccessor, allBindingsAccessor, viewModel) { var $element = $(element), show = ko.utils.unwrapObservable(valueAccessor()), visible = $element.offset().left > -viewModel.width(), $clone = $element.clone(false), taskbarPos = $('.' + viewModel.parentViewModel.taskbarCssClass()).offset(), buttonPos = $('.taskbar-item[data-id="' + viewModel.id + '"]').offset(); if (show) { if (buttonPos && !visible) { // maximise $clone.width(55).height(45).css({'opacity' : 0, 'top' : buttonPos.top + 'px', 'left' : buttonPos.left + 'px'}).appendTo('div.window-container .windows'); $clone.css('position', 'fixed').show().animate( { opacity: 1, left: viewModel.position().split(',')[0], top: viewModel.position().split(',')[1], width: $element.outerWidth(false), height: $element.outerHeight(false) }, 200, function () { $(this).remove(); $element.css({ 'top': viewModel.top(), 'left' : viewModel.left() }); } ); } } else if (taskbarPos) { // minimise $element.css({ 'left' : (-viewModel.width() * 2) + 'px' }); $clone.css('position', 'fixed').appendTo('div.window-container .windows').animate( { opacity: 0, left: taskbarPos.left, top: taskbarPos.top - 40, width: 55, height: 45 }, 400, function () { $(this).remove(); } ); } // save the state in the cookie viewModel.saveState(); } }; }());