diff --git a/Gruntfile.js b/Gruntfile.js index c6c74b7..c7bbb06 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -19,7 +19,7 @@ module.exports = function(grunt) { reporter: "checkstyle", reporterOutput: "jshint.xml" }, - all: ['src/**'], + all: ['src/knockout-bootstrap.js'], } }); diff --git a/build/knockout-bootstrap.min.js b/build/knockout-bootstrap.min.js index efdbf8b..85e11d7 100644 --- a/build/knockout-bootstrap.min.js +++ b/build/knockout-bootstrap.min.js @@ -4,4 +4,4 @@ * Website: http://billpull.github.com/knockout-bootstrap * MIT License http://www.opensource.org/licenses/mit-license.php */ -function s4(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}function guid(){return s4()+s4()+"-"+s4()+"-"+s4()+"-"+s4()+"-"+s4()+s4()+s4()}function setupKoBootstrap(a){a.bindingHandlers.typeahead={init:function(b,c,d){var e=$(b),f=d(),g=a.utils.unwrapObservable(c());e.attr("autocomplete","off").typeahead({source:g,minLength:f.minLength,items:f.items,updater:f.updater})}},a.bindingHandlers.progress={init:function(b,c,d,e){var f=$(b),g=$("
",{"class":"bar","data-bind":"style: { width:"+c()+" }"});f.attr("id",guid()).addClass("progress progress-info").append(g),a.applyBindingsToDescendants(e,f[0])}},a.bindingHandlers.alert={init:function(b,c){var d=$(b),e=a.utils.unwrapObservable(c()),f=$("",{type:"button","class":"close","data-dismiss":"alert"}).html("×"),g=$("").html(e.message);d.addClass("alert alert-"+e.priority).append(f).append(g)}},a.bindingHandlers.tooltip={update:function(b,c){var d,e,f;if(e=a.utils.unwrapObservable(c()),d=$(b),a.isObservable(e.title)){var g=!1;d.on("show.bs.tooltip",function(){g=!0}),d.on("hide.bs.tooltip",function(){g=!1});var h=e.animation||!0;e.title.subscribe(function(){g&&(d.data("bs.tooltip").options.animation=!1,d.tooltip("fixTitle").tooltip("show"),d.data("bs.tooltip").options.animation=h)})}f=d.data("bs.tooltip"),f?$.extend(f.options,e):d.tooltip(e)}},a.bindingHandlers.popover={init:function(b,c,d,e,f){var g=a.utils.unwrapObservable(c()),h=g.title,i=g.template,j=g.data,k="click";g.trigger&&(k=g.trigger),"hover"===k?k="mouseenter mouseleave":"focus"===k&&(k="focus blur");var l=g.placement;if(j)var m=function(){var b=$('');return a.applyBindings({template:i,data:j},b[0]),b};else var m=$("#"+i).html();var n=guid(),o="ko-bs-popover-"+n,p=(f.createChildContext(e),$("",{"class":"ko-popover",id:o}).html(m)),q={content:$(p[0]).outerHtml(),title:h};l&&(q.placement=l),g.container&&(q.container=g.container);var r=$.extend({},a.bindingHandlers.popover.options,q);$(b).bind(k,function(){var a="show",c=$(this);"click"!==k&&(a="toggle"),c.popover(r).popover(a);var d=$("#"+o);if($(".ko-popover").not(d).parents(".popover").remove(),$("#"+o).is(":visible")){var e=$(b).offset().top,f=$(b).offset().left,g=$(b).outerHeight(),h=$(b).outerWidth(),i=$(d).parents(".popover"),j=i.outerHeight(),l=i.outerWidth(),m=10;switch(r.placement){case"left":i.offset({top:e-j/2+g/2,left:f-m-l});break;case"right":i.offset({top:e-j/2+g/2});break;case"top":i.offset({top:e-j-m,left:f-l/2+h/2});break;case"bottom":i.offset({top:e+g+m,left:f-l/2+h/2})}}$(document).on("click",'[data-dismiss="popover"]',function(){c.popover("hide")})})},options:{placement:"right",title:"",html:!0,content:"",trigger:"manual"}}}!function(a){a.fn.outerHtml=function(){if(0==this.length)return!1;var b=this[0],c=b.tagName.toLowerCase();if(b.outerHTML)return b.outerHTML;var d=a.map(b.attributes,function(a){return a.name+'="'+a.value+'"'});return"<"+c+(d.length>0?" "+d.join(" "):"")+">"+b.innerHTML+""+c+">"}}(jQuery),function(a){"function"==typeof define&&define.amd?define(["require","exports","knockout"],function(b,c,d){a(d)}):a(window.ko)}(setupKoBootstrap); \ No newline at end of file +function s4(){"use strict";return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}function guid(){"use strict";return s4()+s4()+"-"+s4()+"-"+s4()+"-"+s4()+"-"+s4()+s4()+s4()}function setupKoBootstrap(a){"use strict";a.bindingHandlers.typeahead={init:function(b,c,d){var e=$(b),f=d(),g=a.utils.unwrapObservable(c());e.attr("autocomplete","off").typeahead({source:g,minLength:f.minLength,items:f.items,updater:f.updater})}},a.bindingHandlers.progress={init:function(b,c,d,e){var f=$(b),g=$("",{"class":"bar","data-bind":"style: { width:"+c()+" }"});f.attr("id",guid()).addClass("progress progress-info").append(g),a.applyBindingsToDescendants(e,f[0])}},a.bindingHandlers.alert={init:function(b,c){var d=$(b),e=a.utils.unwrapObservable(c()),f=$("",{type:"button","class":"close","data-dismiss":"alert"}).html("×"),g=$("").html(e.message);d.addClass("alert alert-"+e.priority).append(f).append(g)}},a.bindingHandlers.tooltip={update:function(b,c){var d,e,f;if(e=a.utils.unwrapObservable(c()),d=$(b),a.isObservable(e.title)){var g=!1;d.on("show.bs.tooltip",function(){g=!0}),d.on("hide.bs.tooltip",function(){g=!1});var h=e.animation||!0;e.title.subscribe(function(){g&&(d.data("bs.tooltip").options.animation=!1,d.tooltip("fixTitle").tooltip("show"),d.data("bs.tooltip").options.animation=h)})}f=d.data("bs.tooltip"),f?$.extend(f.options,e):d.tooltip(e)}},a.bindingHandlers.popover={init:function(b,c,d,e,f){var g=a.utils.unwrapObservable(c()),h=g.title,i=g.template,j=g.data,k="click";g.trigger&&(k=g.trigger),"hover"===k?k="mouseenter mouseleave":"focus"===k&&(k="focus blur");var l,m=g.placement;l=j?function(){var b=$('');return a.applyBindings({template:i,data:j},b[0]),b}:$("#"+i).html();var n=guid(),o="ko-bs-popover-"+n,p=(f.createChildContext(e),$("",{"class":"ko-popover",id:o}).html(l)),q={content:$(p[0]).outerHtml(),title:h};m&&(q.placement=m),g.container&&(q.container=g.container);var r=$.extend({},a.bindingHandlers.popover.options,q);$(b).bind(k,function(){var a="show",c=$(this);"click"!==k&&(a="toggle"),c.popover(r).popover(a);var d=$("#"+o);if($(".ko-popover").not(d).parents(".popover").remove(),$("#"+o).is(":visible")){var e=$(b).offset().top,f=$(b).offset().left,g=$(b).outerHeight(),h=$(b).outerWidth(),i=$(d).parents(".popover"),j=i.outerHeight(),l=i.outerWidth(),m=10;switch(r.placement){case"left":i.offset({top:e-j/2+g/2,left:f-m-l});break;case"right":i.offset({top:e-j/2+g/2});break;case"top":i.offset({top:e-j-m,left:f-l/2+h/2});break;case"bottom":i.offset({top:e+g+m,left:f-l/2+h/2})}}$(document).on("click",'[data-dismiss="popover"]',function(){c.popover("hide")})})},options:{placement:"right",title:"",html:!0,content:"",trigger:"manual"}}}!function(a){"use strict";a.fn.outerHtml=function(){if(0===this.length)return!1;var b=this[0],c=b.tagName.toLowerCase();if(b.outerHTML)return b.outerHTML;var d=a.map(b.attributes,function(a){return a.name+'="'+a.value+'"'});return"<"+c+(d.length>0?" "+d.join(" "):"")+">"+b.innerHTML+""+c+">"}}(jQuery),function(a){"use strict";"function"==typeof define&&define.amd?define(["require","exports","knockout"],function(b,c,d){a(d)}):a(window.ko)}(setupKoBootstrap); \ No newline at end of file diff --git a/src/knockout-bootstrap.js b/src/knockout-bootstrap.js index 66e82cd..2c63198 100644 --- a/src/knockout-bootstrap.js +++ b/src/knockout-bootstrap.js @@ -1,275 +1,287 @@ +/*global: define */ + //UUID function s4() { + "use strict"; return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); + .toString(16) + .substring(1); } function guid() { + "use strict"; return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); } // Outer HTML -(function($){ - $.fn.outerHtml = function() { - if (this.length == 0) return false; - var elem = this[0], name = elem.tagName.toLowerCase(); - if (elem.outerHTML) return elem.outerHTML; - var attrs = $.map(elem.attributes, function(i) { return i.name+'="'+i.value+'"'; }); - return "<"+name+(attrs.length > 0 ? " "+attrs.join(" ") : "")+">"+elem.innerHTML+""+name+">"; - }; +(function ($) { + "use strict"; + $.fn.outerHtml = function () { + if (this.length === 0) { + return false; + } + var elem = this[0], name = elem.tagName.toLowerCase(); + if (elem.outerHTML) { + return elem.outerHTML; + } + var attrs = $.map(elem.attributes, function (i) { return i.name + '="' + i.value + '"'; }); + return "<" + name + (attrs.length > 0 ? " " + attrs.join(" ") : "") + ">" + elem.innerHTML + "" + name + ">"; + }; })(jQuery); -function setupKoBootstrap (koObject) { - // Bind twitter typeahead - koObject.bindingHandlers.typeahead = { - init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - var $element = $(element); - var allBindings = allBindingsAccessor(); - var typeaheadArr = koObject.utils.unwrapObservable(valueAccessor()); +function setupKoBootstrap(koObject) { + "use strict"; + // Bind twitter typeahead + koObject.bindingHandlers.typeahead = { + init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { + var $element = $(element); + var allBindings = allBindingsAccessor(); + var typeaheadArr = koObject.utils.unwrapObservable(valueAccessor()); - $element.attr("autocomplete", "off") + $element.attr("autocomplete", "off") .typeahead({ - 'source': typeaheadArr, - 'minLength': allBindings.minLength, - 'items': allBindings.items, - 'updater': allBindings.updater + 'source': typeaheadArr, + 'minLength': allBindings.minLength, + 'items': allBindings.items, + 'updater': allBindings.updater }); - } - }; + } + }; - // Bind Twitter Progress - koObject.bindingHandlers.progress = { - init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - var $element = $(element); + // Bind Twitter Progress + koObject.bindingHandlers.progress = { + init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { + var $element = $(element); - var bar = $('', { - 'class':'bar', - 'data-bind':'style: { width:' + valueAccessor() + ' }' - }); + var bar = $('', { + 'class': 'bar', + 'data-bind': 'style: { width:' + valueAccessor() + ' }' + }); - $element.attr('id', guid()) + $element.attr('id', guid()) .addClass('progress progress-info') .append(bar); - koObject.applyBindingsToDescendants(viewModel, $element[0]); - } - } - - // Bind Twitter Alert - koObject.bindingHandlers.alert = { - init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - var $element = $(element); - var alertInfo = koObject.utils.unwrapObservable(valueAccessor()); - - var dismissBtn = $('', { - 'type':'button', - 'class':'close', - 'data-dismiss':'alert' - }).html('×'); - - var alertMessage = $('').html(alertInfo.message); - - $element.addClass('alert alert-'+alertInfo.priority) - .append(dismissBtn) - .append(alertMessage); - } - }; - - // Bind Twitter Tooltip - koObject.bindingHandlers.tooltip = { - update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - var $element, options, tooltip; - options = koObject.utils.unwrapObservable(valueAccessor()); - $element = $(element); - - // If the title is an observable, make it auto-updating. - if (koObject.isObservable(options.title)) { - var isToolTipVisible = false; - - $element.on('show.bs.tooltip', function() { - isToolTipVisible = true; - }); - $element.on('hide.bs.tooltip', function () { - isToolTipVisible = false; - }); - - // "true" is the bootstrap default. - var origAnimation = options.animation || true; - options.title.subscribe(function () { - if (isToolTipVisible) { - $element.data('bs.tooltip').options.animation = false; // temporarily disable animation to avoid flickering of the tooltip - $element.tooltip('fixTitle') // call this method to update the title - .tooltip('show'); - $element.data('bs.tooltip').options.animation = origAnimation; - } - }); - } - - tooltip = $element.data('bs.tooltip'); - if (tooltip) { - $.extend(tooltip.options, options); - } else { - $element.tooltip(options); - } - } - }; - - // Bind Twitter Popover - koObject.bindingHandlers.popover = { - init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - // read popover options - var popoverBindingValues = koObject.utils.unwrapObservable(valueAccessor()); - - // set popover title - var popoverTitle = popoverBindingValues.title; - - // set popover template id - var tmplId = popoverBindingValues.template; - - // set data for template - var data = popoverBindingValues.data; - - // set popover trigger - var trigger = 'click'; - - if (popoverBindingValues.trigger) { - trigger = popoverBindingValues.trigger; - } - - // update triggers - if (trigger === 'hover') { - trigger = 'mouseenter mouseleave'; - } else if (trigger === 'focus') { - trigger = 'focus blur'; - } - - // set popover placement - var placement = popoverBindingValues.placement; - - // get template html - if (!data) { - var tmplHtml = $('#' + tmplId).html(); - } else { - var tmplHtml = function () { - var container = $(''); - - koObject.applyBindings({ - template: tmplId, - data: data - }, container[0]); - return container; - }; - } - - // create unique identifier to bind to - var uuid = guid(); - var domId = "ko-bs-popover-" + uuid; - - // create correct binding context - var childBindingContext = bindingContext.createChildContext(viewModel); - - // create DOM object to use for popover content - var tmplDom = $('', { - "class" : "ko-popover", - "id" : domId - }).html(tmplHtml); - - // set content options - var options = { - content: $(tmplDom[0]).outerHtml(), - title: popoverTitle - }; - - if (placement) { - options.placement = placement; - } - - if (popoverBindingValues.container) { - options.container = popoverBindingValues.container; - } - - // Need to copy this, otherwise all the popups end up with the value of the last item - var popoverOptions = $.extend({}, koObject.bindingHandlers.popover.options, options); - - // bind popover to element click - $(element).bind(trigger, function () { - var popoverAction = 'show'; - var popoverTriggerEl = $(this); - - // popovers that hover should be toggled on hover - // not stay there on mouseout - if (trigger !== 'click') { - popoverAction = 'toggle'; - } - - // show/toggle popover - popoverTriggerEl.popover(popoverOptions).popover(popoverAction); - - // hide other popovers and bind knockout to the popover elements - var popoverInnerEl = $('#' + domId); - $('.ko-popover').not(popoverInnerEl).parents('.popover').remove(); - - // if the popover is visible bind the view model to our dom ID - if($('#' + domId).is(':visible')){ - - /* Since bootstrap calculates popover position before template is filled, - * a smaller popover height is used and it appears moved down relative to the trigger element. - * So we have to fix the position after the bind - */ - - var triggerElementPosition = $(element).offset().top; - var triggerElementLeft = $(element).offset().left; - var triggerElementHeight = $(element).outerHeight(); - var triggerElementWidth = $(element).outerWidth(); - - var popover = $(popoverInnerEl).parents('.popover'); - var popoverHeight = popover.outerHeight(); - var popoverWidth = popover.outerWidth(); - var arrowSize = 10; - - switch (popoverOptions.placement) { - case 'left': - popover.offset({ top: triggerElementPosition - popoverHeight / 2 + triggerElementHeight / 2, left: triggerElementLeft - arrowSize - popoverWidth }); - break; - case 'right': - popover.offset({ top: triggerElementPosition - popoverHeight / 2 + triggerElementHeight / 2 }); - break; - case 'top': - popover.offset({ top: triggerElementPosition - popoverHeight - arrowSize, left: triggerElementLeft - popoverWidth / 2 + triggerElementWidth / 2 }); - break; - case 'bottom': - popover.offset({ top: triggerElementPosition + triggerElementHeight + arrowSize, left: triggerElementLeft - popoverWidth / 2 + triggerElementWidth / 2}); - } - } - - - // bind close button to remove popover - $(document).on('click', '[data-dismiss="popover"]', function (e) { - popoverTriggerEl.popover('hide'); - }); - }); - }, - options: { - placement: "right", - title: "", - html: true, - content: "", - trigger: "manual" - } - }; + koObject.applyBindingsToDescendants(viewModel, $element[0]); + } + }; + + // Bind Twitter Alert + koObject.bindingHandlers.alert = { + init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { + var $element = $(element); + var alertInfo = koObject.utils.unwrapObservable(valueAccessor()); + + var dismissBtn = $('', { + 'type': 'button', + 'class': 'close', + 'data-dismiss': 'alert' + }).html('×'); + + var alertMessage = $('').html(alertInfo.message); + + $element.addClass('alert alert-' + alertInfo.priority) + .append(dismissBtn) + .append(alertMessage); + } + }; + + // Bind Twitter Tooltip + koObject.bindingHandlers.tooltip = { + update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { + var $element, options, tooltip; + options = koObject.utils.unwrapObservable(valueAccessor()); + $element = $(element); + + // If the title is an observable, make it auto-updating. + if (koObject.isObservable(options.title)) { + var isToolTipVisible = false; + + $element.on('show.bs.tooltip', function () { + isToolTipVisible = true; + }); + $element.on('hide.bs.tooltip', function () { + isToolTipVisible = false; + }); + + // "true" is the bootstrap default. + var origAnimation = options.animation || true; + options.title.subscribe(function () { + if (isToolTipVisible) { + $element.data('bs.tooltip').options.animation = false; // temporarily disable animation to avoid flickering of the tooltip + $element.tooltip('fixTitle') // call this method to update the title + .tooltip('show'); + $element.data('bs.tooltip').options.animation = origAnimation; + } + }); + } + + tooltip = $element.data('bs.tooltip'); + if (tooltip) { + $.extend(tooltip.options, options); + } else { + $element.tooltip(options); + } + } + }; + + // Bind Twitter Popover + koObject.bindingHandlers.popover = { + init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { + // read popover options + var popoverBindingValues = koObject.utils.unwrapObservable(valueAccessor()); + + // set popover title + var popoverTitle = popoverBindingValues.title; + + // set popover template id + var tmplId = popoverBindingValues.template; + + // set data for template + var data = popoverBindingValues.data; + + // set popover trigger + var trigger = 'click'; + + if (popoverBindingValues.trigger) { + trigger = popoverBindingValues.trigger; + } + + // update triggers + if (trigger === 'hover') { + trigger = 'mouseenter mouseleave'; + } else if (trigger === 'focus') { + trigger = 'focus blur'; + } + + // set popover placement + var placement = popoverBindingValues.placement; + var tmplHtml; + + // get template html + if (!data) { + tmplHtml = $('#' + tmplId).html(); + } else { + tmplHtml = function () { + var container = $(''); + + koObject.applyBindings({ + template: tmplId, + data: data + }, container[0]); + return container; + }; + } + + // create unique identifier to bind to + var uuid = guid(); + var domId = "ko-bs-popover-" + uuid; + + // create correct binding context + var childBindingContext = bindingContext.createChildContext(viewModel); + + // create DOM object to use for popover content + var tmplDom = $('', { + "class": "ko-popover", + "id": domId + }).html(tmplHtml); + + // set content options + var options = { + content: $(tmplDom[0]).outerHtml(), + title: popoverTitle + }; + + if (placement) { + options.placement = placement; + } + + if (popoverBindingValues.container) { + options.container = popoverBindingValues.container; + } + + // Need to copy this, otherwise all the popups end up with the value of the last item + var popoverOptions = $.extend({}, koObject.bindingHandlers.popover.options, options); + + // bind popover to element click + $(element).bind(trigger, function () { + var popoverAction = 'show'; + var popoverTriggerEl = $(this); + + // popovers that hover should be toggled on hover + // not stay there on mouseout + if (trigger !== 'click') { + popoverAction = 'toggle'; + } + + // show/toggle popover + popoverTriggerEl.popover(popoverOptions).popover(popoverAction); + + // hide other popovers and bind knockout to the popover elements + var popoverInnerEl = $('#' + domId); + $('.ko-popover').not(popoverInnerEl).parents('.popover').remove(); + + // if the popover is visible bind the view model to our dom ID + if ($('#' + domId).is(':visible')) { + + /* Since bootstrap calculates popover position before template is filled, + * a smaller popover height is used and it appears moved down relative to the trigger element. + * So we have to fix the position after the bind + */ + + var triggerElementPosition = $(element).offset().top; + var triggerElementLeft = $(element).offset().left; + var triggerElementHeight = $(element).outerHeight(); + var triggerElementWidth = $(element).outerWidth(); + + var popover = $(popoverInnerEl).parents('.popover'); + var popoverHeight = popover.outerHeight(); + var popoverWidth = popover.outerWidth(); + var arrowSize = 10; + + switch (popoverOptions.placement) { + case 'left': + popover.offset({ top: triggerElementPosition - popoverHeight / 2 + triggerElementHeight / 2, left: triggerElementLeft - arrowSize - popoverWidth }); + break; + case 'right': + popover.offset({ top: triggerElementPosition - popoverHeight / 2 + triggerElementHeight / 2 }); + break; + case 'top': + popover.offset({ top: triggerElementPosition - popoverHeight - arrowSize, left: triggerElementLeft - popoverWidth / 2 + triggerElementWidth / 2 }); + break; + case 'bottom': + popover.offset({ top: triggerElementPosition + triggerElementHeight + arrowSize, left: triggerElementLeft - popoverWidth / 2 + triggerElementWidth / 2 }); + } + } + + + // bind close button to remove popover + $(document).on('click', '[data-dismiss="popover"]', function (e) { + popoverTriggerEl.popover('hide'); + }); + }); + }, + options: { + placement: "right", + title: "", + html: true, + content: "", + trigger: "manual" + } + }; } -(function(factory) { +(function (factory) { + "use strict"; // Support multiple loading scenarios - if (typeof define === 'function' && define['amd']) { + if (typeof define === 'function' && define.amd) { // AMD anonymous module - define(["require", "exports", "knockout"], function(require, exports, knockout) { - factory(knockout); + define(["require", "exports", "knockout"], function (require, exports, knockout) { + factory(knockout); }); } else { // No module loader (plain