From db49e98f371061c099ac871b4b973e9a3c63e9f7 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 24 Apr 2018 23:41:01 +1000 Subject: [PATCH 1/9] Load bootstrap.min.js --- InvenTree/static/script/bootstrap.min.js | 7 +++++++ InvenTree/templates/base.html | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 InvenTree/static/script/bootstrap.min.js diff --git a/InvenTree/static/script/bootstrap.min.js b/InvenTree/static/script/bootstrap.min.js new file mode 100644 index 0000000000..9bcd2fccae --- /dev/null +++ b/InvenTree/static/script/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under the MIT license + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html index bd8058424b..512d545992 100644 --- a/InvenTree/templates/base.html +++ b/InvenTree/templates/base.html @@ -16,7 +16,7 @@ - + {% block head %} From 9004086632162a4975a9193a1c2ac932a603c755 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Apr 2018 00:09:25 +1000 Subject: [PATCH 2/9] Add button to launch modal form --- InvenTree/part/forms.py | 2 +- InvenTree/part/templates/part/index.html | 34 +++++++++++++++++++ .../templates/part/partial_category_new.html | 1 + InvenTree/part/views.py | 2 +- InvenTree/templates/base.html | 13 ++++--- InvenTree/templates/modal_form.html | 18 ++++++++++ 6 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 InvenTree/part/templates/part/partial_category_new.html create mode 100644 InvenTree/templates/modal_form.html diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index 109b1961b6..2a66103168 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -45,7 +45,7 @@ class EditCategoryForm(forms.ModelForm): self.helper.form_id = 'id-edit-part-form' self.helper.form_method = 'post' - self.helper.add_input(Submit('submit', 'Submit')) + #self.helper.add_input(Submit('submit', 'Submit')) class Meta: model = PartCategory diff --git a/InvenTree/part/templates/part/index.html b/InvenTree/part/templates/part/index.html index 94d939f27e..bc912f346b 100644 --- a/InvenTree/part/templates/part/index.html +++ b/InvenTree/part/templates/part/index.html @@ -5,6 +5,18 @@ {% include "part/cat_link.html" with category=category %} + + + + {% if children.all|length > 0 %}

Part Categories

{% include "part/category_subcategories.html" with children=children %} @@ -23,3 +35,25 @@ {% endblock %} +{% block javascript %} + + + +{% endblock %} diff --git a/InvenTree/part/templates/part/partial_category_new.html b/InvenTree/part/templates/part/partial_category_new.html new file mode 100644 index 0000000000..acec8357bd --- /dev/null +++ b/InvenTree/part/templates/part/partial_category_new.html @@ -0,0 +1 @@ +{% include 'modal_form.html' with title='Create new Part Catagory' %} \ No newline at end of file diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 0b2b768a93..9b3ac4df09 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -132,7 +132,7 @@ class CategoryDelete(DeleteView): class CategoryCreate(CreateView): model = PartCategory - template_name = 'part/category_new.html' + template_name = 'part/partial_category_new.html' form_class = EditCategoryForm def get_context_data(self, **kwargs): diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html index 512d545992..02ea1d788b 100644 --- a/InvenTree/templates/base.html +++ b/InvenTree/templates/base.html @@ -13,11 +13,6 @@ - - - - - {% block head %} {% endblock %} @@ -39,5 +34,13 @@ InvenTree {% endblock %} + + + + +{% block javascript %} + +{% endblock %} + \ No newline at end of file diff --git a/InvenTree/templates/modal_form.html b/InvenTree/templates/modal_form.html new file mode 100644 index 0000000000..bdc1529170 --- /dev/null +++ b/InvenTree/templates/modal_form.html @@ -0,0 +1,18 @@ +{% load static %} + +
+ {% csrf_token %} + + + +
\ No newline at end of file From 8bc4050d05630c5a8061f687c42d4c531ff207e3 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Apr 2018 12:46:58 +1000 Subject: [PATCH 3/9] Modal form is working for 'create new category' - Had to set form.helper.form_tag to False (so we could control form tags manually) - Created a 'json' model view --- InvenTree/part/forms.py | 5 +-- InvenTree/part/templates/part/index.html | 29 +++++++++++++++--- InvenTree/part/urls.py | 2 +- InvenTree/part/views.py | 39 ++++++++++++++++++++++++ InvenTree/templates/modal_form.html | 33 ++++++++++---------- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index 2a66103168..df23f1bdcf 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -42,9 +42,10 @@ class EditCategoryForm(forms.ModelForm): super(EditCategoryForm, self).__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_id = 'id-edit-part-form' - self.helper.form_method = 'post' + #self.helper.form_id = 'id-edit-part-form' + #self.helper.form_method = 'post' + self.helper.form_tag = False #self.helper.add_input(Submit('submit', 'Submit')) class Meta: diff --git a/InvenTree/part/templates/part/index.html b/InvenTree/part/templates/part/index.html index bc912f346b..d0bda70df7 100644 --- a/InvenTree/part/templates/part/index.html +++ b/InvenTree/part/templates/part/index.html @@ -38,21 +38,42 @@ {% block javascript %} diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 31129ad529..51ee0736a4 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -48,7 +48,7 @@ part_bom_urls = [ part_urls = [ # Create a new category - url(r'^category/new/?', views.CategoryCreate.as_view(), name='category-create'), + url(r'^category/new/?', views.CategoryCreateJson.as_view(), name='category-create'), # Create a new part url(r'^new/?', views.PartCreate.as_view(), name='part-create'), diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 9b3ac4df09..fa571d343c 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from django.template.loader import render_to_string + from django.shortcuts import get_object_or_404 from django.http import HttpResponseRedirect +from django.http import JsonResponse from django.views.generic import DetailView, ListView from django.views.generic.edit import UpdateView, DeleteView, CreateView @@ -130,6 +133,42 @@ class CategoryDelete(DeleteView): return HttpResponseRedirect(self.get_object().get_absolute_url()) +class CategoryCreateJson(CreateView): + model = PartCategory + template_name = 'part/partial_category_new.html' + form_class = EditCategoryForm + + def renderJsonResponse(self, request, form, data): + + context = {'form': form} + + data['html_form'] = render_to_string(self.template_name, + context, + request=request) + + return JsonResponse(data) + + def post(self, request): + form = self.form_class(request.POST) + + data = {} + + if form.is_valid(): + form.save() + data['form_valid'] = True + else: + data['form_valid'] = False + + return self.renderJsonResponse(request, form, data) + + def get(self, request, *args, **kwargs): + form = self.form_class() + + data = {} + + return self.renderJsonResponse(request, form, data) + + class CategoryCreate(CreateView): model = PartCategory template_name = 'part/partial_category_new.html' diff --git a/InvenTree/templates/modal_form.html b/InvenTree/templates/modal_form.html index bdc1529170..d5d4a8bad5 100644 --- a/InvenTree/templates/modal_form.html +++ b/InvenTree/templates/modal_form.html @@ -1,18 +1,17 @@ -{% load static %} - -
- {% csrf_token %} - - - + + {% csrf_token %} + + +
\ No newline at end of file From d88da28871efd82ae1a245e4266b3632655122d6 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Apr 2018 14:10:56 +1000 Subject: [PATCH 4/9] Generic AjaxCreateView - Allows for same form to be posted --- InvenTree/InvenTree/views.py | 65 +++++++++++++++++++ InvenTree/part/templates/part/index.html | 34 ++++++---- .../templates/part/partial_category_new.html | 1 - InvenTree/part/urls.py | 2 +- InvenTree/part/views.py | 49 +++----------- InvenTree/templates/modal_form.html | 6 +- 6 files changed, 98 insertions(+), 59 deletions(-) create mode 100644 InvenTree/InvenTree/views.py delete mode 100644 InvenTree/part/templates/part/partial_category_new.html diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py new file mode 100644 index 0000000000..5293c8b0ad --- /dev/null +++ b/InvenTree/InvenTree/views.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.template.loader import render_to_string +from django.http import JsonResponse + +from django.views.generic import UpdateView, CreateView, DeleteView + + +class AjaxView(object): + + ajax_form_action = '' + ajax_form_title = '' + ajax_submit_text = 'Submit' + + def getAjaxTemplate(self): + if hasattr(self, 'ajax_template_name'): + return self.ajax_template_name + else: + return self.template_name + + + def renderJsonResponse(self, request, form): + + context = {'form': form, + 'form_action': self.ajax_form_action, + 'form_title': self.ajax_form_title, + 'submit_text': self.ajax_submit_text, + } + + data = {'form_valid': form.is_valid()} + + data['html_form'] = render_to_string( + self.getAjaxTemplate(), + context, + request=request + ) + + return JsonResponse(data) + + +class AjaxCreateView(AjaxView, CreateView): + + def post(self, request): + + if request.is_ajax(): + form = self.form_class(request.POST) + + if form.is_valid(): + form.save() + + return self.renderJsonResponse(request, form) + + else: + return super(CreateView, self).post(request) + + def get(self, request): + + if request.is_ajax(): + form = self.form_class() + + return self.renderJsonResponse(request, form) + + else: + return super(CreateView, self).post(request) diff --git a/InvenTree/part/templates/part/index.html b/InvenTree/part/templates/part/index.html index d0bda70df7..df51ce51f2 100644 --- a/InvenTree/part/templates/part/index.html +++ b/InvenTree/part/templates/part/index.html @@ -12,11 +12,6 @@ - - {% if children.all|length > 0 %}

Part Categories

{% include "part/category_subcategories.html" with children=children %} @@ -26,9 +21,9 @@ {% include "part/category_parts.html" with parts=parts %}
- - - + @@ -49,7 +44,17 @@ $(document).ready(function () { $("#modal-cat").modal("show"); }, success: function (data) { - $("#modal-cat .modal-content").html(data.html_form); + if (data.html_form) { + $("#modal-cat .modal-content").html(data.html_form); + } + else { + alert('JSON response missing form data'); + $("#modal-cat").modal("hide"); + } + }, + error: function (xhr, ajaxOptions, thrownError) { + alert("Error requesting form data:\n" + thrownError); + $("#modal-cat").modal("hide"); } }); }); @@ -63,12 +68,15 @@ $(document).ready(function () { type: form.attr('method'), dataType: 'json', success: function (data) { - if (data.form_valid) { - alert("Success!"); - } - else { + if (data.html_form) { $("#modal-cat .modal-content").html(data.html_form); } + else { + alert('No form data!'); + } + }, + error: function (xhr, ajaxOptions, thrownError) { + alert("Error submitting form data:\n" + thrownError); } }); diff --git a/InvenTree/part/templates/part/partial_category_new.html b/InvenTree/part/templates/part/partial_category_new.html deleted file mode 100644 index acec8357bd..0000000000 --- a/InvenTree/part/templates/part/partial_category_new.html +++ /dev/null @@ -1 +0,0 @@ -{% include 'modal_form.html' with title='Create new Part Catagory' %} \ No newline at end of file diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 51ee0736a4..31129ad529 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -48,7 +48,7 @@ part_bom_urls = [ part_urls = [ # Create a new category - url(r'^category/new/?', views.CategoryCreateJson.as_view(), name='category-create'), + url(r'^category/new/?', views.CategoryCreate.as_view(), name='category-create'), # Create a new part url(r'^new/?', views.PartCreate.as_view(), name='part-create'), diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index fa571d343c..b7ed0c11ef 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.template.loader import render_to_string - from django.shortcuts import get_object_or_404 from django.http import HttpResponseRedirect -from django.http import JsonResponse + +from django.urls import reverse_lazy from django.views.generic import DetailView, ListView from django.views.generic.edit import UpdateView, DeleteView, CreateView @@ -20,6 +19,7 @@ from .forms import EditBomItemForm from .forms import EditSupplierPartForm +from InvenTree.views import AjaxCreateView class PartIndex(ListView): model = Part @@ -133,45 +133,12 @@ class CategoryDelete(DeleteView): return HttpResponseRedirect(self.get_object().get_absolute_url()) -class CategoryCreateJson(CreateView): +class CategoryCreate(AjaxCreateView): model = PartCategory - template_name = 'part/partial_category_new.html' - form_class = EditCategoryForm - - def renderJsonResponse(self, request, form, data): - - context = {'form': form} - - data['html_form'] = render_to_string(self.template_name, - context, - request=request) - - return JsonResponse(data) - - def post(self, request): - form = self.form_class(request.POST) - - data = {} - - if form.is_valid(): - form.save() - data['form_valid'] = True - else: - data['form_valid'] = False - - return self.renderJsonResponse(request, form, data) - - def get(self, request, *args, **kwargs): - form = self.form_class() - - data = {} - - return self.renderJsonResponse(request, form, data) - - -class CategoryCreate(CreateView): - model = PartCategory - template_name = 'part/partial_category_new.html' + ajax_form_action = reverse_lazy('category-create') + ajax_form_title = 'Create new part category' + ajax_template_name = 'modal_form.html' + template_name = 'part/category_new.html' form_class = EditCategoryForm def get_context_data(self, **kwargs): diff --git a/InvenTree/templates/modal_form.html b/InvenTree/templates/modal_form.html index d5d4a8bad5..5140c08a86 100644 --- a/InvenTree/templates/modal_form.html +++ b/InvenTree/templates/modal_form.html @@ -1,10 +1,10 @@ -
+ {% csrf_token %}
\ No newline at end of file From ab05c6ce1e1f7e834f474c00d73fbf4fdde15441 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Apr 2018 15:27:23 +1000 Subject: [PATCH 5/9] Updated AjaxCreateView - Return PK of newly-created object - Populate fields with initial values as necessary --- InvenTree/InvenTree/views.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 5293c8b0ad..aea55a598a 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -20,7 +20,7 @@ class AjaxView(object): return self.template_name - def renderJsonResponse(self, request, form): + def renderJsonResponse(self, request, form, data={}): context = {'form': form, 'form_action': self.ajax_form_action, @@ -28,8 +28,6 @@ class AjaxView(object): 'submit_text': self.ajax_submit_text, } - data = {'form_valid': form.is_valid()} - data['html_form'] = render_to_string( self.getAjaxTemplate(), context, @@ -46,20 +44,27 @@ class AjaxCreateView(AjaxView, CreateView): if request.is_ajax(): form = self.form_class(request.POST) - if form.is_valid(): - form.save() + data = {'form_valid': form.is_valid()} - return self.renderJsonResponse(request, form) + if form.is_valid(): + obj = form.save() + + # Return the PK of the newly-created object + data['pk'] = obj.pk + + return self.renderJsonResponse(request, form, data) else: return super(CreateView, self).post(request) def get(self, request): + response = super(CreateView, self).get(request) + if request.is_ajax(): - form = self.form_class() + form = self.form_class(initial=self.get_initial()) return self.renderJsonResponse(request, form) else: - return super(CreateView, self).post(request) + return response From e3b2bd71293867f80e2128d8789ef4716af6bd71 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Apr 2018 15:28:57 +1000 Subject: [PATCH 6/9] Abstracted jquery code to load a modal form - static/scripts/modal_form.js - bindModalForm function takes care of the mechanics --- InvenTree/part/templates/part/index.html | 50 ++------------------ InvenTree/static/script/modal_form.js | 59 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 47 deletions(-) create mode 100644 InvenTree/static/script/modal_form.js diff --git a/InvenTree/part/templates/part/index.html b/InvenTree/part/templates/part/index.html index df51ce51f2..a6f5ca4e29 100644 --- a/InvenTree/part/templates/part/index.html +++ b/InvenTree/part/templates/part/index.html @@ -32,56 +32,12 @@ {% block javascript %} + + diff --git a/InvenTree/static/script/modal_form.js b/InvenTree/static/script/modal_form.js new file mode 100644 index 0000000000..cc1f609b3a --- /dev/null +++ b/InvenTree/static/script/modal_form.js @@ -0,0 +1,59 @@ +/* Bind a button to launch a modal form and handle AJAX requests */ +function bindModalForm(modal, button, url, data) { + $(button).click(function () { + $.ajax({ + url: url, // Where to request the data from + type: 'get', // GET request + data: data, // Any additional context data (e.g. initial values) + dataType: 'json', + beforeSend: function() { + $(modal).modal('show'); + }, + success: function(response) { + if (response.html_form) { + var target = modal + ' .modal-content'; + $(target).html(response.html_form); + } else { + alert('JSON response missing form data'); + $(modal).modal('hide'); + } + }, + error: function (xhr, ajaxOptions, thrownError) { + alert('Error requesting form data:\n' + thrownError); + $(modal).modal('hide'); + } + }); + }); + + $(modal).on('submit', '.js-modal-form', function() { + var form = $(this); + + $.ajax({ + url: form.attr('action'), + data: form.serialize(), + type: form.attr('method'), + dataType: 'json', + success: function (response) { + if (response.form_valid) { + alert("Success!"); + $(modal).modal('hide'); + } + else if (response.html_form) { + var target = modal + ' .modal-content'; + $(target).html(response.html_form); + } + else { + alert('JSON response missing form data'); + $(modal).modal('hide'); + } + }, + error: function (xhr, ajaxOptions, thrownError) { + alert("Error posting form data:\n" + thrownError); + $(modal).modal('hide'); + } + }); + + // Override the default form submit functionality + return false; + }); +} \ No newline at end of file From 99743c6bd0e18b01ba459b79896ab684d26da359 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 25 Apr 2018 15:34:12 +1000 Subject: [PATCH 7/9] Modal form now working on multiple pages - Part index - Part category detail page(s) --- .../part/templates/part/category_detail.html | 28 +++++++++++++++++-- InvenTree/part/templates/part/index.html | 2 +- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/InvenTree/part/templates/part/category_detail.html b/InvenTree/part/templates/part/category_detail.html index 7e91c82a59..c9e012c35c 100644 --- a/InvenTree/part/templates/part/category_detail.html +++ b/InvenTree/part/templates/part/category_detail.html @@ -22,9 +22,9 @@ {% endif %}
- - - + @@ -39,4 +39,26 @@
+ + +{% endblock %} + +{% block javascript %} + + + + + {% endblock %} \ No newline at end of file diff --git a/InvenTree/part/templates/part/index.html b/InvenTree/part/templates/part/index.html index a6f5ca4e29..9881c0079b 100644 --- a/InvenTree/part/templates/part/index.html +++ b/InvenTree/part/templates/part/index.html @@ -37,7 +37,7 @@ From 55e7f365df75489d562b2f141f0b595906ff98a0 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 26 Apr 2018 08:18:53 +1000 Subject: [PATCH 8/9] Added AjaxUpdateView class Also cleaned up the modal form javascript --- InvenTree/InvenTree/views.py | 38 ++++++++++++++++++--- InvenTree/part/templates/part/bom.html | 10 +++++- InvenTree/part/templates/part/index.html | 6 +++- InvenTree/static/script/modal_form.js | 43 +++++++++++------------- 4 files changed, 68 insertions(+), 29 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index aea55a598a..acb4707ebc 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -39,7 +39,7 @@ class AjaxView(object): class AjaxCreateView(AjaxView, CreateView): - def post(self, request): + def post(self, request, *args, **kwargs): if request.is_ajax(): form = self.form_class(request.POST) @@ -55,9 +55,39 @@ class AjaxCreateView(AjaxView, CreateView): return self.renderJsonResponse(request, form, data) else: - return super(CreateView, self).post(request) + return super(CreateView, self).post(request, *args, **kwargs) - def get(self, request): + def get(self, request, *args, **kwargs): + + response = super(CreateView, self).get(request, *args, **kwargs) + + if request.is_ajax(): + form = self.form_class(initial=self.get_initial()) + + return self.renderJsonResponse(request, form) + + else: + return response + + +class AjaxUpdateView(AjaxView, UpdateView): + + def post(self, request, *args, **kwargs): + + if request.is_ajax(): + form = self.form_class(request.POST) + + data = {'form_valid': form.is_valid()} + + if form.is_valid(): + obj = form.save() + + return self.renderJsonResponse(request, form, data) + + else: + return super(UpdateView, self).post(request, *args, **kwargs) + + def get(self, request, *args, **kwargs): response = super(CreateView, self).get(request) @@ -67,4 +97,4 @@ class AjaxCreateView(AjaxView, CreateView): return self.renderJsonResponse(request, form) else: - return response + return super(UpdateView, self).get(request, *args, **kwargss) diff --git a/InvenTree/part/templates/part/bom.html b/InvenTree/part/templates/part/bom.html index 0e9e28b6b1..96653a9518 100644 --- a/InvenTree/part/templates/part/bom.html +++ b/InvenTree/part/templates/part/bom.html @@ -19,7 +19,9 @@ {{ sub_part.name }} {{ sub_part.description }} {{ bom_item.quantity }} - Edit + + + {% endwith %} {% endfor %} @@ -31,4 +33,10 @@
+{% endblock %} + +{% block javascript %} + + + {% endblock %} \ No newline at end of file diff --git a/InvenTree/part/templates/part/index.html b/InvenTree/part/templates/part/index.html index 9881c0079b..65ac4b9edd 100644 --- a/InvenTree/part/templates/part/index.html +++ b/InvenTree/part/templates/part/index.html @@ -37,7 +37,11 @@ diff --git a/InvenTree/static/script/modal_form.js b/InvenTree/static/script/modal_form.js index cc1f609b3a..7834bf620d 100644 --- a/InvenTree/static/script/modal_form.js +++ b/InvenTree/static/script/modal_form.js @@ -1,28 +1,25 @@ -/* Bind a button to launch a modal form and handle AJAX requests */ -function bindModalForm(modal, button, url, data) { - $(button).click(function () { - $.ajax({ - url: url, // Where to request the data from - type: 'get', // GET request - data: data, // Any additional context data (e.g. initial values) - dataType: 'json', - beforeSend: function() { - $(modal).modal('show'); - }, - success: function(response) { - if (response.html_form) { - var target = modal + ' .modal-content'; - $(target).html(response.html_form); - } else { - alert('JSON response missing form data'); - $(modal).modal('hide'); - } - }, - error: function (xhr, ajaxOptions, thrownError) { - alert('Error requesting form data:\n' + thrownError); +function launchModalForm(modal, url, data) { + $.ajax({ + url: url, // Where to request the data from + type: 'get', // GET request + data: data, // Any additional context data (e.g. initial values) + dataType: 'json', + beforeSend: function() { + $(modal).modal('show'); + }, + success: function(response) { + if (response.html_form) { + var target = modal + ' .modal-content'; + $(target).html(response.html_form); + } else { + alert('JSON response missing form data'); $(modal).modal('hide'); } - }); + }, + error: function (xhr, ajaxOptions, thrownError) { + alert('Error requesting form data:\n' + thrownError); + $(modal).modal('hide'); + } }); $(modal).on('submit', '.js-modal-form', function() { From e395d89a7fd30a2b5d0795a052a9817145b0afb0 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 26 Apr 2018 09:26:43 +1000 Subject: [PATCH 9/9] Fix issues for AjaxUdpateView - Works properly now! - Can edit BOM item inline - Can add BOM item inline too --- InvenTree/InvenTree/views.py | 12 ++++--- InvenTree/part/forms.py | 5 +-- InvenTree/part/templates/part/bom.html | 32 +++++++++++++++---- .../part/templates/part/category_detail.html | 10 +++--- InvenTree/part/templates/part/index.html | 2 -- InvenTree/part/views.py | 10 ++++-- InvenTree/static/script/modal_form.js | 2 +- 7 files changed, 50 insertions(+), 23 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index acb4707ebc..07a09da1c4 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from django.template.loader import render_to_string from django.http import JsonResponse +from django.shortcuts import get_object_or_404 from django.views.generic import UpdateView, CreateView, DeleteView @@ -75,7 +76,7 @@ class AjaxUpdateView(AjaxView, UpdateView): def post(self, request, *args, **kwargs): if request.is_ajax(): - form = self.form_class(request.POST) + form = self.form_class(request.POST, instance=self.get_object()) data = {'form_valid': form.is_valid()} @@ -85,16 +86,17 @@ class AjaxUpdateView(AjaxView, UpdateView): return self.renderJsonResponse(request, form, data) else: - return super(UpdateView, self).post(request, *args, **kwargs) + response = super(UpdateView, self).post(request, *args, **kwargs) + return response def get(self, request, *args, **kwargs): - response = super(CreateView, self).get(request) + response = super(UpdateView, self).get(request, *args, **kwargs) if request.is_ajax(): - form = self.form_class(initial=self.get_initial()) + form = self.form_class(instance = self.get_object()) return self.renderJsonResponse(request, form) else: - return super(UpdateView, self).get(request, *args, **kwargss) + return response diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index df23f1bdcf..f4b9c11b06 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -63,10 +63,11 @@ class EditBomItemForm(forms.ModelForm): super(EditBomItemForm, self).__init__(*args, **kwargs) self.helper = FormHelper() - self.helper.form_id = 'id-edit-part-form' + #self.helper.form_id = 'id-edit-part-form' self.helper.form_method = 'post' - self.helper.add_input(Submit('submit', 'Submit')) + self.helper.form_tag = False + #self.helper.add_input(Submit('submit', 'Submit')) class Meta: model = BomItem diff --git a/InvenTree/part/templates/part/bom.html b/InvenTree/part/templates/part/bom.html index 96653a9518..8322243a0b 100644 --- a/InvenTree/part/templates/part/bom.html +++ b/InvenTree/part/templates/part/bom.html @@ -1,9 +1,16 @@ {% extends "part/part_base.html" %} - +{% load static %} {% block details %} {% include 'part/tabs.html' with tab='bom' %} + +

Bill of Materials

@@ -18,9 +25,9 @@ - + {% endwith %} @@ -28,9 +35,7 @@
{{ sub_part.name }} {{ sub_part.description }}{{ bom_item.quantity }}{{ bom_item.quantity }} - +
- - - +
{% endblock %} @@ -39,4 +44,19 @@ + + {% endblock %} \ No newline at end of file diff --git a/InvenTree/part/templates/part/category_detail.html b/InvenTree/part/templates/part/category_detail.html index c9e012c35c..ec87512e6b 100644 --- a/InvenTree/part/templates/part/category_detail.html +++ b/InvenTree/part/templates/part/category_detail.html @@ -54,10 +54,12 @@ diff --git a/InvenTree/part/templates/part/index.html b/InvenTree/part/templates/part/index.html index 65ac4b9edd..c4de5560c5 100644 --- a/InvenTree/part/templates/part/index.html +++ b/InvenTree/part/templates/part/index.html @@ -40,8 +40,6 @@ $(document).ready(function () { $(".js-create-cat").click(function() { launchModalForm("#modal-cat", "{% url 'category-create' %}"); }); - - //bindModalForm('#modal-cat', '.js-create-cat', "{% url 'category-create' %}"); }); diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index b7ed0c11ef..e7f515abb2 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -19,7 +19,7 @@ from .forms import EditBomItemForm from .forms import EditSupplierPartForm -from InvenTree.views import AjaxCreateView +from InvenTree.views import AjaxCreateView, AjaxUpdateView class PartIndex(ListView): model = Part @@ -168,10 +168,12 @@ class BomItemDetail(DetailView): template_name = 'part/bom-detail.html' -class BomItemCreate(CreateView): +class BomItemCreate(AjaxCreateView): model = BomItem form_class = EditBomItemForm template_name = 'part/bom-create.html' + ajax_template_name = 'modal_form.html' + ajax_form_title = 'Create BOM item' def get_initial(self): # Look for initial values @@ -186,10 +188,12 @@ class BomItemCreate(CreateView): return initials -class BomItemEdit(UpdateView): +class BomItemEdit(AjaxUpdateView): model = BomItem form_class = EditBomItemForm template_name = 'part/bom-edit.html' + ajax_template_name = 'modal_form.html' + ajax_form_title = 'Edit BOM item' class BomItemDelete(DeleteView): diff --git a/InvenTree/static/script/modal_form.js b/InvenTree/static/script/modal_form.js index 7834bf620d..9a85c55a63 100644 --- a/InvenTree/static/script/modal_form.js +++ b/InvenTree/static/script/modal_form.js @@ -26,7 +26,7 @@ function launchModalForm(modal, url, data) { var form = $(this); $.ajax({ - url: form.attr('action'), + url: url, data: form.serialize(), type: form.attr('method'), dataType: 'json',