diff --git a/assets/css/bootstrap-wizard-lib/LICENSE b/assets/css/bootstrap-wizard-lib/LICENSE new file mode 100644 index 0000000..6aee4f8 --- /dev/null +++ b/assets/css/bootstrap-wizard-lib/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2013 Panopta, Andrew Moffat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/assets/css/bootstrap-wizard-lib/bootstrap-wizard.css b/assets/css/bootstrap-wizard-lib/bootstrap-wizard.css new file mode 100644 index 0000000..4713675 --- /dev/null +++ b/assets/css/bootstrap-wizard-lib/bootstrap-wizard.css @@ -0,0 +1,182 @@ +/* WIZARD GENERAL */ +.wizard { + display:none; +} + +.wizard-dialog {} +.wizard-content {} + +.wizard-body { + padding: 0; + margin: 0; +} + +/* WIZARD HEADER */ +.wizard-header { + padding: 9px 15px; + border-bottom: 0; +} + +.wizard-header h3 { + margin: 0; + line-height: 35px; + display: inline; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-family: inherit; + font-weight: bold; + text-rendering: optimizelegibility; + color: rgb(51, 51, 51); +} + +.wizard-subtitle { + font-weight:bold; + color:#AFAFAF; + padding-left:20px; +} + + +/* WIZARD NAVIGATION */ +.wizard-steps { + width: 28%; + background-color: #f5f5f5; + border-bottom-left-radius: 6px; + position: relative; +} + +.wizard-nav-container { + padding-bottom: 30px; +} + +.wizard-nav-list { + margin-bottom: 0; +} + +.wizard-nav-link .glyphicon-chevron-right { + float:right; + margin-top:12px; + margin-right:-6px; + opacity:.25; +} + +li.wizard-nav-item.active .glyphicon-chevron-right { + opacity:1; +} + +li.wizard-nav-item { + line-height:40px; +} + +.wizard-nav-list > li > a { + background-color:#f5f5f5; + padding:3px 15px 3px 20px; + cursor:default; + color:#B4B4B4; +} + +.wizard-nav-list > li > a:hover { + background-color: transparent; +} + +.wizard-nav-list > li.already-visited > a.wizard-nav-link { + color:#08C; + cursor:pointer; +} + +.wizard-nav-list > li.active > a.wizard-nav-link { + color:white; +} + +.wizard-nav-item .already-visited .active { + background-color:#08C; +} + +.wizard-nav-list li.active > a { + background-color:#08C; +} + + +/* WIZARD CONTENT */ +.wizard-body form { + padding: 0; + margin: 0; +} + +/* WIZARD PROGRESS BAR */ +.wizard-progress-container { + margin-top: 20px; + padding: 15px; + width: 100%; + position: absolute; + bottom: 0; +} + +.wizard-card-container { + margin-left: 28%; +} + +/* WIZARD CARDS */ +.wizard-error, +.wizard-failure, +.wizard-success, +.wizard-loading, +.wizard-card { + border-top: 1px solid #EEE; + display:none; + padding:35px; + padding-top:20px; + overflow-y:auto; + + /* + position:relative; + height:300px; + margin-right: 5px; + */ +} + +.wizard-card-overlay { + overflow-y: initial; +} + +.wizard-card > h3 { + margin-top:0; + margin-bottom:20px; + font-size:21px; + line-height:40px; + font-weight:normal; +} + +/* WIZARD FOOTER */ +.wizard-footer { + padding:0; +} + +.wizard-buttons-container { + padding:20px; +} + +.wizard-cancel { + margin-left: 12px; +} + +/* Inner Card */ +.wizard-input-section { + margin-bottom:20px; +} + +.wizard-dialog .popover.error-popover { + background-color:#F2DEDE; + color:#B94A48; + border-color:#953B39; +} + +.wizard-dialog .popover.error-popover .arrow::after { + border-right-color:#F2DEDE; +} + +.wizard-dialog .popover.error-popover .popover-title { + display:none; +} + +.wizard-dialog .popover.error-popover .arrow { + border-right-color:#953B39; +} \ No newline at end of file diff --git a/assets/fonts/FontAwesome.otf b/assets/fonts/FontAwesome.otf new file mode 100644 index 0000000..81c9ad9 Binary files /dev/null and b/assets/fonts/FontAwesome.otf differ diff --git a/assets/fonts/fontawesome-webfont.eot b/assets/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..84677bc Binary files /dev/null and b/assets/fonts/fontawesome-webfont.eot differ diff --git a/assets/fonts/fontawesome-webfont.ttf b/assets/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..96a3639 Binary files /dev/null and b/assets/fonts/fontawesome-webfont.ttf differ diff --git a/assets/fonts/fontawesome-webfont.woff b/assets/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..628b6a5 Binary files /dev/null and b/assets/fonts/fontawesome-webfont.woff differ diff --git a/assets/fonts/glyphicons-halflings-regular.eot b/assets/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 0000000..4a4ca86 Binary files /dev/null and b/assets/fonts/glyphicons-halflings-regular.eot differ diff --git a/assets/fonts/glyphicons-halflings-regular.svg b/assets/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 0000000..e3e2dc7 --- /dev/null +++ b/assets/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/fonts/glyphicons-halflings-regular.ttf b/assets/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000..67fa00b Binary files /dev/null and b/assets/fonts/glyphicons-halflings-regular.ttf differ diff --git a/assets/fonts/glyphicons-halflings-regular.woff b/assets/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000..8c54182 Binary files /dev/null and b/assets/fonts/glyphicons-halflings-regular.woff differ diff --git a/assets/js/bootstrap-wizard-lib/LICENSE b/assets/js/bootstrap-wizard-lib/LICENSE new file mode 100644 index 0000000..6aee4f8 --- /dev/null +++ b/assets/js/bootstrap-wizard-lib/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2013 Panopta, Andrew Moffat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/assets/js/bootstrap-wizard-lib/bootstrap-wizard.js b/assets/js/bootstrap-wizard-lib/bootstrap-wizard.js new file mode 100644 index 0000000..b0bc7f7 --- /dev/null +++ b/assets/js/bootstrap-wizard-lib/bootstrap-wizard.js @@ -0,0 +1,1171 @@ +/* + * Copyright (C) 2013 Panopta, Andrew Moffat + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +(function ($) { + $.fn.wizard = function(args) { + return new Wizard(this, args); + }; + + $.fn.wizard.logging = false; + + var WizardCard = function(wizard, card, index, prev, next) { + this.wizard = wizard; + this.index = index; + this.prev = prev; + this.next = next; + this.el = card; + this.title = card.find("h3").first().text(); + this.name = card.data("cardname") || this.title; + + this.nav = this._createNavElement(this.title, index); + + this._disabled = false; + this._loaded = false; + this._events = {}; + }; + + WizardCard.prototype = { + select: function() { + this.log("selecting"); + if (!this.isSelected()) { + this.nav.addClass("active"); + this.el.show(); + + if (!this._loaded) { + this.trigger("loaded"); + this.reload(); + } + + this.trigger("selected"); + } + + + /* + * this is ugly, but we're handling the changing of the wizard's + * buttons here, in the WizardCard select. so when a card is + * selected, we're figuring out if we're the first card or the + * last card and changing the wizard's buttons via the guts of + * the wizard + * + * ideally this logic should be encapsulated by some wizard methods + * that we can call from here, instead of messing with the guts + */ + var w = this.wizard; + + // The back button is only disabled on this first card... + w.backButton.toggleClass("disabled", this.index == 0); + + if (this.index >= w._cards.length-1) { + this.log("on last card, changing next button to submit"); + + w.changeNextButton(w.args.buttons.submitText, "btn-success"); + w._readyToSubmit = true; + w.trigger("readySubmit"); + } + else { + w._readyToSubmit = false; + w.changeNextButton(w.args.buttons.nextText, "btn-primary"); + } + + return this; + }, + + _createNavElement: function(name, i) { + var li = $('
  • '); + var a = $(''); + a.data("navindex", i); + li.append(a); + a.append(' '); + a.append(name); + return li; + }, + + markVisited: function() { + this.log("marking as visited"); + this.nav.addClass("already-visited"); + this.trigger("markVisited"); + return this; + }, + + unmarkVisited: function() { + this.log("unmarking as visited"); + this.nav.removeClass("already-visited"); + this.trigger("unmarkVisited"); + return this; + }, + + deselect: function() { + this.nav.removeClass("active"); + this.el.hide(); + this.trigger("deselect"); + return this; + }, + + enable: function() { + this.log("enabling"); + + // Issue #38 Hiding navigation link when hide card + // Awaiting approval + // + // this.nav.removeClass('hide'); + + this.nav.addClass("active"); + this._disabled = false; + this.trigger("enabled"); + return this; + }, + + disable: function(hideCard) { + this.log("disabling"); + this._disabled = true; + this.nav.removeClass("active already-visited"); + if (hideCard) { + this.el.hide(); + // Issue #38 Hiding navigation link when hide card + // Awaiting approval + // + // this.nav.addClass('hide'); + } + this.trigger("disabled"); + return this; + }, + + isDisabled: function() { + return this._disabled; + }, + + alreadyVisited: function() { + return this.nav.hasClass("already-visited"); + }, + + isSelected: function() { + return this.nav.hasClass("active"); + }, + + reload: function() { + this._loaded = true; + this.trigger("reload"); + return this; + }, + + on: function() { + return this.wizard.on.apply(this, arguments); + }, + + trigger: function() { + this.callListener("on"+arguments[0]); + return this.wizard.trigger.apply(this, arguments); + }, + + /* + * displays an alert box on the current card + */ + toggleAlert: function(msg, toggle) { + this.log("toggling alert to: " + toggle); + + toggle = typeof(toggle) == "undefined" ? true : toggle; + + if (toggle) {this.trigger("showAlert");} + else {this.trigger("hideAlert");} + + var div; + var alert = this.el.children("h3").first().next("div.alert"); + + if (alert.length == 0) { + /* + * we're hiding anyways, so no need to create anything. + * we'll do that if we ever are actually showing the alert + */ + if (!toggle) {return this;} + + this.log("couldn't find existing alert div, creating one"); + div = $("
    "); + div.addClass("alert"); + div.addClass("hide"); + div.insertAfter(this.el.find("h3").first()); + } + else { + this.log("found existing alert div"); + div = alert.first(); + } + + if (toggle) { + if (msg != null) { + this.log("setting alert msg to", msg); + div.html(msg); + } + div.show(); + } + else { + div.hide(); + } + return this; + }, + + /* + * this looks for event handlers embedded into the html of the + * wizard card itself, in the form of a data- attribute + */ + callListener: function(name) { + // a bug(?) in jquery..can't access data- if name is camelCase + name = name.toLowerCase(); + + this.log("looking for listener " + name); + var listener = window[this.el.data(name)]; + if (listener) { + this.log("calling listener " + name); + var wizard = this.wizard; + + try { + var vret = listener(this); + } + catch (e) { + this.log("exception calling listener " + name + ": ", e); + } + } + else { + this.log("didn't find listener " + name); + } + }, + + problem: function(toggle) { + this.nav.find("a").toggleClass("wizard-step-error", toggle); + }, + + validate: function() { + var failures = false; + var self = this; + + /* + * run all the validators embedded on the inputs themselves + */ + this.el.find("[data-validate]").each(function(i, el) { + self.log("validating individiual inputs"); + el = $(el); + + var v = el.data("validate"); + if (!v) {return;} + + var ret = { + status: true, + title: "Error", + msg: "" + }; + + var vret = window[v](el); + $.extend(ret, vret); + + // Add-On + // This allows the use of a INPUT+BTN used as one according to boostrap layout + // for the wizard it is required to add an id with btn-(ID of Input) + // this will make sure the popover is drawn on the correct element + if ( $('#btn-' + el.attr('id')).length === 1 ) { + el = $('#btn-' + el.attr('id')); + } + + if (!ret.status) { + failures = true; + + // Updated to show error on correct form-group + el.parents("div.form-group").toggleClass("has-error", true); + + // This allows the use of a INPUT+BTN used as one according to boostrap layout + // for the wizard it is required to add an id with btn-(ID of Input) + // this will make sure the popover is drawn on the correct element + if ( $('#btn-' + el.attr('id')).length === 1 ) { + el = $('#btn-' + el.attr('id')); + } + + self.wizard.errorPopover(el, ret.msg); + } else { + el.parents("div.form-group").toggleClass("has-error", false); + + // This allows the use of a INPUT+BTN used as one according to boostrap layout + // for the wizard it is required to add an id with btn-(ID of Input) + // this will make sure the popover is drawn on the correct element + if ( $('#btn-' + el.attr('id')).length === 1 ) { + el = $('#btn-' + el.attr('id')); + } + + try { + el.popover("destroy"); + } + /* + * older versions of bootstrap don't have a destroy call + * for popovers + */ + catch (e) { + el.popover("hide"); + } + } + }); + this.log("after validating inputs, failures is", failures); + + /* + * run the validator embedded in the card + */ + var cardValidator = window[this.el.data("validate")]; + if (cardValidator) { + this.log("running html-embedded card validator"); + var cardValidated = cardValidator(this); + if (typeof(cardValidated) == "undefined" || cardValidated == null) { + cardValidated = true; + } + if (!cardValidated) failures = true; + this.log("after running html-embedded card validator, failures is", failures); + } + + /* + * run the validate listener + */ + this.log("running listener validator"); + var listenerValidated = this.trigger("validate"); + if (typeof(listenerValidated) == "undefined" || listenerValidated == null) { + listenerValidated = true; + } + if (!listenerValidated) failures = true; + this.log("after running listener validator, failures is", failures); + + var validated = !failures; + if (validated) { + this.log("validated, calling listeners"); + this.trigger("validated"); + } + else { + this.log("invalid"); + this.trigger("invalid"); + } + return validated; + }, + + log: function() { + if (!window.console || !$.fn.wizard.logging) {return;} + var prepend = "card '"+this.name+"': "; + var args = [prepend]; + args.push.apply(args, arguments); + + console.log.apply(console, args); + }, + + isActive: function() { + return this.nav.hasClass("active"); + } + }; + + Wizard = function(markup, args) { + + /* TEMPLATE */ + this.wizard_template = [ + '' + ]; + + this.args = { + keyboard: true, + backdrop: true, + show: false, + submitUrl: "", + showCancel: false, + showClose: true, + progressBarCurrent: false, + increaseHeight: 0, + contentHeight: 400, + contentWidth: '50%', + buttons: { + cancelText: "Cancel", + nextText: "Next", + backText: "Back", + submitText: "Submit", + submittingText: "Submitting...", + }, + formClass: "form-horizontal" + }; + + $.extend(this.args, args || {}); + + this._create(markup); + }; + + Wizard.prototype = { + log: function() { + if (!window.console || !$.fn.wizard.logging) {return;} + var prepend = "wizard "+this.el.id+": "; + var args = [prepend]; + args.push.apply(args, arguments); + console.log.apply(console, args); + }, + + _create: function(markup) { + this.markup = $(markup); + this.title = this.markup.data('title'); + this.submitCards = this.markup.find(".wizard-error,.wizard-failure,.wizard-success,.wizard-loading"); + this.el = $(this.wizard_template.join('\n')); + $('body').append(this.el); + + this.modal = this.el.modal({ + keyboard: this.args.keyboard, + show: this.args.show, + backdrop: this.args.backdrop + }); + + this.dimensions = { + contentHeight: this.args.contentHeight, + contentWidth: this.args.contentWidth + }; + this.dialog = this.modal.find('.wizard-dialog'); + this.content = this.modal.find('.wizard-content'); + this.header = this.modal.find('.wizard-header'); + this.body = this.modal.find('.wizard-body'); + this.wizardSteps = this.modal.find('.wizard-steps'); + this.wizardCards = this.modal.find('.wizard-cards'); + this.wizardCardContainer = this.modal.find('.wizard-card-container'); + this.wizardCardContainer + .append(this.markup.find('.wizard-card')) + .append(this.submitCards); + this.navContainer = this.modal.find('.wizard-nav-container'); + this.navList = this.modal.find('.wizard-nav-list'); + this.progressContainer = this.modal.find('.wizard-progress-container'); + this.progress = this.progressContainer.find('.progress-bar'); + this.closeButton = this.modal.find('button.wizard-close.close'); + this.cardsContainer = this.modal.find('wizard-cards-container'); + this.form = this.modal.find('form'); + this.footer = this.modal.find(".wizard-footer"); + this.cancelButton = this.footer.find(".wizard-cancel"); + this.backButton = this.footer.find(".wizard-back"); + this.nextButton = this.footer.find(".wizard-next"); + + this._cards = []; + this.cards = {}; + this._readyToSubmit = false; + this.percentComplete = 0; + this._submitting = false; + this._events = {}; + this._firstShow = true; + + this._createCards(); + + this.nextButton.click(this, this._handleNextClick); + this.backButton.click(this, this._handleBackClick); + + this.cancelButton.text(this.args.buttons.cancelText); + this.backButton.text(this.args.buttons.backText); + this.nextButton.text(this.args.buttons.nextText); + + // Apply Form Class(es) + this.form.addClass(this.args.formClass); + + // Register Array Holder for popovers + this.popovers = []; + + var self = this; + var _close = function() { + self.reset(); + self.close(); + self.trigger("closed"); + }; + + // Register Close Button + this.closeButton.click(_close); + this.cancelButton.click(_close); + + this.wizardSteps.on("click", "li.already-visited a.wizard-nav-link", this, + function(event) { + var index = parseInt($(event.target).data("navindex")); + event.data.setCard(index); + }); + + if ( this.title.length != 0 ) { + this.setTitle(this.title); + } + + this.on("submit", this._defaultSubmit); + + // Set Modal Dimensions + this.autoDimensions(); + }, + + autoDimensions: function() { + // DO NOT REMOVE DISPLAY ; Temporary display is required for calculation + this.modal.css('display', 'block'); + + this.dimensions.header = this.header.outerHeight(true); + + // Navigation Pane is dyanmic build on card content + // Navigation Pane === BASE Inner Content Height + this.dimensions.navigation = this.wizardSteps.outerHeight(true); + if ( this.dimensions.navigation < this.dimensions.contentHeight ) { + this.dimensions.navigation = this.dimensions.contentHeight; + this.navContainer.height( (this.dimensions.contentHeight-30) - this.progressContainer.outerHeight(true)); + } + + // Dimension Alias ( Body Height === (Navigation Height) ) + this.dimensions.body = this.dimensions.navigation; + + // Apply OuterHeight of navigation to it's parent wizardSteps + this.wizardSteps.height(this.dimensions.body); + + // Modal Height === (Header + Content) + this.dimensions.modal = (this.dimensions.header + this.dimensions.navigation); + this.content.height(this.dimensions.modal + 'px'); + this.dialog.width(this.dimensions.contentWidth); + + this.body.height(this.dimensions.body + 'px'); + this.wizardCards.height(this.dimensions.body + 'px'); + + // Footer Height + this.dimensions.footer = this.footer.outerHeight(true); + + // Card Container === (Body - Footer) + this.dimensions.cardContainer = (this.dimensions.body - this.dimensions.footer); + this.wizardCardContainer.height(this.dimensions.cardContainer); + + // Reposition + this.dimensions.offset = ($(window).height() - this.dialog.height()) / 2; + this.dialog.css({ + 'margin-top': this.dimensions.offset + 'px', + 'padding-top': 0 + }); + + // DO NOT REMOVE NEXT LINE + this.modal.css('display', ''); + }, + + setTitle: function(title) { + this.log("setting title to", title); + this.modal.find(".wizard-title").first().text(title); + return this; + }, + + setSubtitle: function(title) { + this.log("setting subtitle to", title); + this.modal.find(".wizard-subtitle").first().text(title); + return this; + }, + + errorPopover: function(el, msg, allowHtml) { + this.log("launching popover on", el); + allowHtml = typeof allowHtml !== "undefined" ? allowHtml : false; + var popover = el.popover({ + content: msg, + trigger: "manual", + html: allowHtml, + container: el.parents('.form-group') + }).addClass("error-popover").popover("show").next(".popover"); + + el.parents('.form-group').find('.popover').addClass("error-popover"); + + this.popovers.push(el); + + return popover; + }, + + destroyPopover: function(pop) { + pop = $(pop); + + /* + * this is the element that the popover was created for + */ + try { + pop.popover("destroy"); + } + /* + * older versions of bootstrap don't have a destroy call + * for popovers + */ + catch (e) { + pop.popover("hide"); + } + }, + + hidePopovers: function(el) { + this.log("hiding all popovers"); + var self = this; + + $.each(this.popovers, function(i, p) { + self.destroyPopover(p); + }); + + this.modal.find('.has-error').removeClass('has-error'); + this.popovers = []; + }, + + eachCard: function(fn) { + $.each(this._cards, fn); + return this; + }, + + getActiveCard: function() { + this.log("getting active card"); + var currentCard = null; + + $.each(this._cards, function(i, card) { + if (card.isActive()) { + currentCard = card; + return false; + } + }); + if (currentCard) {this.log("found active card", currentCard);} + else {this.log("couldn't find an active card");} + return currentCard; + }, + + changeNextButton: function(text, cls) { + this.log("changing next button, text: " + text, "class: " + cls); + if (typeof(cls) != "undefined") { + this.nextButton.removeClass("btn-success btn-primary"); + } + + if (cls) { + this.nextButton.addClass(cls); + } + this.nextButton.text(text); + return this; + }, + + hide: function() { + this.log("hiding"); + this.modal.modal("hide"); + return this; + }, + + close: function() { + this.log("closing"); + this.modal.modal("hide"); + return this; + }, + + + show: function(modalOptions) { + this.log("showing"); + if (this._firstShow) { + this.setCard(0); + this._firstShow = false; + } + if (this.args.showCancel) { + this.cancelButton.show(); + } else { + this.cancelButton.hide(); + } + if (this.args.showClose) { this.closeButton.show(); } + this.modal.modal('show'); + + return this; + }, + + on: function(name, fn) { + this.log("adding listener to event " + name); + this._events[name] = fn; + return this; + }, + + trigger: function() { + var name = arguments[0]; + var args = Array.prototype.slice.call(arguments); + args.shift(); + args.unshift(this); + + this.log("firing event " + name); + var handler = this._events[name]; + if (handler === undefined && this.wizard !== undefined) { + handler = this.wizard._events[name]; + } + var ret = null; + + if (typeof(handler) == "function") { + this.log("found event handler, calling " + name); + try { + ret = handler.apply(this, args); + } + catch (e) { + this.log("event handler " + name + " had an exception"); + } + } + else { + this.log("couldn't find an event handler for " + name); + } + return ret; + }, + + + reset: function() { + this.log("resetting"); + + this.updateProgressBar(0); + this.hideSubmitCards(); + + this.setCard(0); + this.lockCards(); + + this.enableNextButton(); + this.showButtons(); + + this.hidePopovers(); + + this.trigger("reset"); + return this; + }, + + /* + * this handles switching to the next card or previous card, taking + * care to skip over disabled cards + */ + _abstractIncrementStep: function(direction, getNext) { + var current = this.getActiveCard(); + var next; + + if (current) { + /* + * loop until we find a card that isn't disabled + */ + this.log("searching for valid next card"); + while (true) { + next = getNext(current); + if (next) { + this.log("looking at card", next.index); + if (next.isDisabled()) { + this.log("card " + next.index + " is disabled/locked, continuing"); + current = next; + continue; + } + else { + return this.setCard(current.index+direction); + } + } + else { + this.log("next card is not defined, breaking"); + break; + } + } + } + else { + this.log("current card is undefined"); + } + }, + + + incrementCard: function() { + this.log("incrementing card"); + var card = this._abstractIncrementStep(1, function(current){return current.next;}); + this.trigger("incrementCard"); + return card; + }, + + decrementCard: function() { + this.log("decrementing card"); + var card = this._abstractIncrementStep(-1, function(current){return current.prev;}); + this.trigger("decrementCard"); + return card; + }, + + setCard: function(i) { + this.log("setting card to " + i); + this.hideSubmitCards(); + var currentCard = this.getActiveCard(); + + if (this._submitting) { + this.log("we're submitting the wizard already, can't change cards"); + return currentCard; + } + + var newCard = this._cards[i]; + if (newCard) { + if (newCard.isDisabled()) { + this.log("new card is currently disabled, returning"); + return currentCard; + } + + if (currentCard) { + + /* + * here, we're only validating if we're going forward, + * not if we're going backwards in a step + */ + if (i > currentCard.index) { + var cardToValidate = currentCard; + var ok = false; + + /* + * we need to loop over every card between our current + * card and the card that we clicked, and re-validate + * them. if there's an error, we need to select the + * first card to have an error + */ + while (cardToValidate.index != newCard.index) { + /* + * unless we're validating the card that we're + * leaving, we need to select the card, so that + * any validators that trigger errorPopovers can + * display correctly + */ + if (cardToValidate.index != currentCard.index) { + cardToValidate.prev.deselect(); + cardToValidate.prev.markVisited(); + cardToValidate.select(); + } + ok = cardToValidate.validate(); + if (!ok) { + return cardToValidate; + } + cardToValidate = cardToValidate.next; + } + + cardToValidate.prev.deselect(); + cardToValidate.prev.markVisited(); + } + + currentCard.deselect(); + currentCard.markVisited(); + } + + newCard.select(); + + if (this.args.progressBarCurrent) { + this.percentComplete = i * 100.0 / this._cards.length; + this.updateProgressBar(this.percentComplete); + } + else { + var lastPercent = this.percentComplete; + this.percentComplete = i * 100.0 / this._cards.length; + this.percentComplete = Math.max(lastPercent, this.percentComplete); + this.updateProgressBar(this.percentComplete); + } + + return newCard; + } + else { + this.log("couldn't find card " + i); + } + }, + + updateProgressBar: function(percent) { + this.log("updating progress to " + percent + "%"); + this.progress.css({width: percent + "%"}); + this.percentComplete = percent; + + this.trigger("progressBar", percent); + + if (percent == 100) { + this.log("progress is 100, animating progress bar"); + this.progressContainer.find('.progress').addClass("active"); + } + else if (percent == 0) { + this.log("progress is 0, disabling animation"); + this.progressContainer.find('.progress').removeClass("active"); + } + }, + + getNextCard: function() { + var currentCard = this.getActiveCard(); + if (currentCard) return currentCard.next; + }, + + lockCards: function() { + this.log("locking nav cards"); + this.eachCard(function(i,card){card.unmarkVisited();}); + return this; + }, + + disableCards: function() { + this.log("disabling all nav cards"); + this.eachCard(function(i,card){card.disable();}); + return this; + }, + + enableCards: function() { + this.log("enabling all nav cards"); + this.eachCard(function(i,card){card.enable();}); + return this; + }, + + hideCards: function() { + this.log("hiding cards"); + this.eachCard(function(i,card){card.deselect();}); + this.hideSubmitCards(); + return this; + }, + + hideButtons: function() { + this.log("hiding buttons"); + this.cancelButton.hide(); + this.closeButton.hide(); + this.nextButton.hide(); + this.backButton.hide(); + return this; + }, + + showButtons: function() { + this.log("showing buttons"); + if (this.args.showCancel) { + this.cancelButton.show(); + } else { + this.cancelButton.hide(); + } + if (this.args.showClose) { this.closeButton.show(); }; + this.nextButton.show(); + this.backButton.show(); + return this; + }, + + getCard: function(el) { + var cardDOMEl = $(el).parents(".wizard-card").first()[0]; + if (cardDOMEl) { + var foundCard = null; + this.eachCard(function(i, card) { + if (cardDOMEl == card.el[0]) { + foundCard = card; + return false; + } + return true; + }); + return foundCard; + } + else { + return null; + } + }, + + _createCards: function() { + var prev = null; + var next = null; + var currentCard = null; + var wizard = this; + var self = this; + + self.log("Creating Cards"); + + var cards = this.modal.find(".wizard-cards .wizard-card"); + $.each(cards, function(i, card) { + card = $(card); + + prev = currentCard; + currentCard = new WizardCard(wizard, card, i, prev, next); + self._cards.push(currentCard); + if (currentCard.name) { + self.cards[currentCard.name] = currentCard; + } + if (prev) {prev.next = currentCard;} + + self.modal.find(".wizard-steps .wizard-nav-list").append(currentCard.nav); + }); + }, + + showSubmitCard: function(name) { + this.log("showing "+name+" submit card"); + + var card = this.el.find(".wizard-"+name); + if (card.length) { + this.hideCards(); + this.el.find(".wizard-"+name).show(); + } + else { + this.log("couldn't find submit card "+name); + } + }, + + hideSubmitCard: function(name) { + this.log("hiding "+name+" submit card"); + this.el.find(".wizard-"+name).hide(); + }, + + hideSubmitCards: function() { + var wizard = this; + $.each(["success", "error", "failure", "loading"], function(i, name) { + wizard.hideSubmitCard(name); + }); + }, + + enableNextButton: function() { + this.log("enabling next button"); + this.nextButton.removeAttr("disabled"); + return this; + }, + + disableNextButton: function() { + this.log("disabling next button"); + this.nextButton.attr("disabled", "disabled"); + return this; + }, + + serializeArray: function() { + var form = this.form.serializeArray(); + this.form.find('input[disabled][data-serialize="1"]').each(function() { + formObj = { + name: $(this).attr('name'), + value: $(this).val() + }; + + form.push(formObj); + }); + + return form; + }, + + serialize: function() { + var form = this.form.serialize(); + this.form.find('input[disabled][data-serialize="1"]').each(function() { + form = form + '&' + $(this).attr('name') + '=' + $(this).val(); + }); + + return form; + }, + + find: function(selector) { + return this.modal.find(selector); + }, + + + /* + * the next 3 functions are to be called by the custom submit event + * handler. the idea is that after you make an ajax call to submit + * your wizard data (or whatever it is you want to do at the end of + * the wizard), you call one of these 3 handlers to display a specific + * card for either success, failure, or error + */ + submitSuccess: function() { + this.log("submit success"); + this._submitting = false; + this.showSubmitCard("success"); + this.trigger("submitSuccess"); + }, + + submitFailure: function() { + this.log("submit failure"); + this._submitting = false; + this.showSubmitCard("failure"); + this.trigger("submitFailure"); + }, + + submitError: function() { + this.log("submit error"); + this._submitting = false; + this.showSubmitCard("error"); + this.trigger("submitError"); + }, + + + _submit: function() { + this.log("submitting wizard"); + this._submitting = true; + + this.lockCards(); + this.cancelButton.hide(); + this.closeButton.hide(); + this.backButton.hide(); + + this.showSubmitCard("loading"); + this.updateProgressBar(100); + + this.changeNextButton(this.args.buttons.submittingText, false); + this.disableNextButton(); + + var ret = this.trigger("submit"); + this.trigger("loading"); + }, + + _onNextClick: function() { + this.log("handling 'next' button click"); + var currentCard = this.getActiveCard(); + if (this._readyToSubmit && currentCard.validate()) { + this._submit(); + } + else { + currentCard = this.incrementCard(); + } + }, + + _onBackClick: function() { + this.log("handling 'back' button click"); + var currentCard = this.decrementCard(); + }, + + _handleNextClick: function(event) { + var wizard = event.data; + wizard._onNextClick.call(wizard); + }, + + _handleBackClick: function(event) { + var wizard = event.data; + wizard._onBackClick.call(wizard); + }, + + + /* + * this function is attached by default to the wizard's "submit" event. + * if you choose to implement your own custom submit logic, you should + * copy how this function works + */ + _defaultSubmit: function(wizard) { + $.ajax({ + type: "POST", + url: wizard.args.submitUrl, + data: wizard.serialize(), + dataType: "json" + }).done(function(response) { + wizard.submitSuccess(); + wizard.hideButtons(); + wizard.updateProgressBar(0); + }).fail(function() { + wizard.submitFailure(); + wizard.hideButtons(); + }); + } + }; + + +}(window.jQuery)); diff --git a/assets/js/bootstrap-wizard-lib/bootstrap-wizard.min.js b/assets/js/bootstrap-wizard-lib/bootstrap-wizard.min.js new file mode 100644 index 0000000..898bb73 --- /dev/null +++ b/assets/js/bootstrap-wizard-lib/bootstrap-wizard.min.js @@ -0,0 +1 @@ +(function(e){e.fn.wizard=function(e){return new Wizard(this,e)};e.fn.wizard.logging=false;var t=function(e,t,n,r,i){this.wizard=e;this.index=n;this.prev=r;this.next=i;this.el=t;this.title=t.find("h3").first().text();this.name=t.data("cardname")||this.title;this.nav=this._createNavElement(this.title,n);this._disabled=false;this._loaded=false;this._events={}};t.prototype={select:function(){this.log("selecting");if(!this.isSelected()){this.nav.addClass("active");this.el.show();if(!this._loaded){this.trigger("loaded");this.reload()}this.trigger("selected")}var e=this.wizard;e.backButton.toggleClass("disabled",this.index==0);if(this.index>=e._cards.length-1){this.log("on last card, changing next button to submit");e.changeNextButton(e.args.buttons.submitText,"btn-success");e._readyToSubmit=true;e.trigger("readySubmit")}else{e._readyToSubmit=false;e.changeNextButton(e.args.buttons.nextText,"btn-primary")}return this},_createNavElement:function(t,n){var r=e('
  • ');var i=e('');i.data("navindex",n);r.append(i);i.append(' ');i.append(t);return r},markVisited:function(){this.log("marking as visited");this.nav.addClass("already-visited");this.trigger("markVisited");return this},unmarkVisited:function(){this.log("unmarking as visited");this.nav.removeClass("already-visited");this.trigger("unmarkVisited");return this},deselect:function(){this.nav.removeClass("active");this.el.hide();this.trigger("deselect");return this},enable:function(){this.log("enabling");this.nav.addClass("active");this._disabled=false;this.trigger("enabled");return this},disable:function(e){this.log("disabling");this._disabled=true;this.nav.removeClass("active already-visited");if(e){this.el.hide()}this.trigger("disabled");return this},isDisabled:function(){return this._disabled},alreadyVisited:function(){return this.nav.hasClass("already-visited")},isSelected:function(){return this.nav.hasClass("active")},reload:function(){this._loaded=true;this.trigger("reload");return this},on:function(){return this.wizard.on.apply(this,arguments)},trigger:function(){this.callListener("on"+arguments[0]);return this.wizard.trigger.apply(this,arguments)},toggleAlert:function(t,n){this.log("toggling alert to: "+n);n=typeof n=="undefined"?true:n;if(n){this.trigger("showAlert")}else{this.trigger("hideAlert")}var r;var i=this.el.children("h3").first().next("div.alert");if(i.length==0){if(!n){return this}this.log("couldn't find existing alert div, creating one");r=e("
    ");r.addClass("alert");r.addClass("hide");r.insertAfter(this.el.find("h3").first())}else{this.log("found existing alert div");r=i.first()}if(n){if(t!=null){this.log("setting alert msg to",t);r.html(t)}r.show()}else{r.hide()}return this},callListener:function(e){e=e.toLowerCase();this.log("looking for listener "+e);var t=window[this.el.data(e)];if(t){this.log("calling listener "+e);var n=this.wizard;try{var r=t(this)}catch(i){this.log("exception calling listener "+e+": ",i)}}else{this.log("didn't find listener "+e)}},problem:function(e){this.nav.find("a").toggleClass("wizard-step-error",e)},validate:function(){var t=false;var n=this;this.el.find("[data-validate]").each(function(r,i){n.log("validating individiual inputs");i=e(i);var s=i.data("validate");if(!s){return}var o={status:true,title:"Error",msg:""};var u=window[s](i);e.extend(o,u);if(e("#btn-"+i.attr("id")).length===1){i=e("#btn-"+i.attr("id"))}if(!o.status){t=true;i.parents("div.form-group").toggleClass("has-error",true);if(e("#btn-"+i.attr("id")).length===1){i=e("#btn-"+i.attr("id"))}n.wizard.errorPopover(i,o.msg)}else{i.parents("div.form-group").toggleClass("has-error",false);if(e("#btn-"+i.attr("id")).length===1){i=e("#btn-"+i.attr("id"))}try{i.popover("destroy")}catch(a){i.popover("hide")}}});this.log("after validating inputs, failures is",t);var r=window[this.el.data("validate")];if(r){this.log("running html-embedded card validator");var i=r(this);if(typeof i=="undefined"||i==null){i=true}if(!i)t=true;this.log("after running html-embedded card validator, failures is",t)}this.log("running listener validator");var s=this.trigger("validate");if(typeof s=="undefined"||s==null){s=true}if(!s)t=true;this.log("after running listener validator, failures is",t);var o=!t;if(o){this.log("validated, calling listeners");this.trigger("validated")}else{this.log("invalid");this.trigger("invalid")}return o},log:function(){if(!window.console||!e.fn.wizard.logging){return}var t="card '"+this.name+"': ";var n=[t];n.push.apply(n,arguments);console.log.apply(console,n)},isActive:function(){return this.nav.hasClass("active")}};Wizard=function(t,n){this.wizard_template=['"];this.args={keyboard:true,backdrop:true,show:false,submitUrl:"",showCancel:false,showClose:true,progressBarCurrent:false,increaseHeight:0,contentHeight:300,contentWidth:'50%',buttons:{cancelText:"Cancel",nextText:"Next",backText:"Back",submitText:"Submit",submittingText:"Submitting..."},formClass:"form-horizontal"};e.extend(this.args,n||{});this._create(t)};Wizard.prototype={log:function(){if(!window.console||!e.fn.wizard.logging){return}var t="wizard "+this.el.id+": ";var n=[t];n.push.apply(n,arguments);console.log.apply(console,n)},_create:function(t){this.markup=e(t);this.title=this.markup.data("title");this.submitCards=this.markup.find(".wizard-error,.wizard-failure,.wizard-success,.wizard-loading");this.el=e(this.wizard_template.join("\n"));e("body").append(this.el);this.modal=this.el.modal({keyboard:this.args.keyboard,show:this.args.show,backdrop:this.args.backdrop});this.dimensions={contentHeight:this.args.contentHeight,contentWidth:this.args.contentWidth};this.dialog=this.modal.find(".wizard-dialog");this.content=this.modal.find(".wizard-content");this.header=this.modal.find(".wizard-header");this.body=this.modal.find(".wizard-body");this.wizardSteps=this.modal.find(".wizard-steps");this.wizardCards=this.modal.find(".wizard-cards");this.wizardCardContainer=this.modal.find(".wizard-card-container");this.wizardCardContainer.append(this.markup.find(".wizard-card")).append(this.submitCards);this.navContainer=this.modal.find(".wizard-nav-container");this.navList=this.modal.find(".wizard-nav-list");this.progressContainer=this.modal.find(".wizard-progress-container");this.progress=this.progressContainer.find(".progress-bar");this.closeButton=this.modal.find("button.wizard-close.close");this.cardsContainer=this.modal.find("wizard-cards-container");this.form=this.modal.find("form");this.footer=this.modal.find(".wizard-footer");this.cancelButton=this.footer.find(".wizard-cancel");this.backButton=this.footer.find(".wizard-back");this.nextButton=this.footer.find(".wizard-next");this._cards=[];this.cards={};this._readyToSubmit=false;this.percentComplete=0;this._submitting=false;this._events={};this._firstShow=true;this._createCards();this.nextButton.click(this,this._handleNextClick);this.backButton.click(this,this._handleBackClick);this.cancelButton.text(this.args.buttons.cancelText);this.backButton.text(this.args.buttons.backText);this.nextButton.text(this.args.buttons.nextText);this.form.addClass(this.args.formClass);this.popovers=[];var n=this;var r=function(){n.reset();n.close();n.trigger("closed")};this.closeButton.click(r);this.cancelButton.click(r);this.wizardSteps.on("click","li.already-visited a.wizard-nav-link",this,function(t){var n=parseInt(e(t.target).data("navindex"));t.data.setCard(n)});if(this.title.length!=0){this.setTitle(this.title)}this.on("submit",this._defaultSubmit);this.autoDimensions()},autoDimensions:function(){this.modal.css("display","block");this.dimensions.header=this.header.outerHeight(true);this.dimensions.navigation=this.wizardSteps.outerHeight(true);if(this.dimensions.navigationt.index){var r=t;var i=false;while(r.index!=n.index){if(r.index!=t.index){r.prev.deselect();r.prev.markVisited();r.select()}i=r.validate();if(!i){return r}r=r.next}r.prev.deselect();r.prev.markVisited()}t.deselect();t.markVisited()}n.select();if(this.args.progressBarCurrent){this.percentComplete=e*100/this._cards.length;this.updateProgressBar(this.percentComplete)}else{var s=this.percentComplete;this.percentComplete=e*100/this._cards.length;this.percentComplete=Math.max(s,this.percentComplete);this.updateProgressBar(this.percentComplete)}return n}else{this.log("couldn't find card "+e)}},updateProgressBar:function(e){this.log("updating progress to "+e+"%");this.progress.css({width:e+"%"});this.percentComplete=e;this.trigger("progressBar",e);if(e==100){this.log("progress is 100, animating progress bar");this.progressContainer.find(".progress").addClass("active")}else if(e==0){this.log("progress is 0, disabling animation");this.progressContainer.find(".progress").removeClass("active")}},getNextCard:function(){var e=this.getActiveCard();if(e)return e.next},lockCards:function(){this.log("locking nav cards");this.eachCard(function(e,t){t.unmarkVisited()});return this},disableCards:function(){this.log("disabling all nav cards");this.eachCard(function(e,t){t.disable()});return this},enableCards:function(){this.log("enabling all nav cards");this.eachCard(function(e,t){t.enable()});return this},hideCards:function(){this.log("hiding cards");this.eachCard(function(e,t){t.deselect()});this.hideSubmitCards();return this},hideButtons:function(){this.log("hiding buttons");this.cancelButton.hide();this.closeButton.hide();this.nextButton.hide();this.backButton.hide();return this},showButtons:function(){this.log("showing buttons");if(this.args.showCancel){this.cancelButton.show()}else{this.cancelButton.hide()}if(this.args.showClose){this.closeButton.show()}this.nextButton.show();this.backButton.show();return this},getCard:function(t){var n=e(t).parents(".wizard-card").first()[0];if(n){var r=null;this.eachCard(function(e,t){if(n==t.el[0]){r=t;return false}return true});return r}else{return null}},_createCards:function(){var n=null;var r=null;var i=null;var s=this;var o=this;o.log("Creating Cards");var u=this.modal.find(".wizard-cards .wizard-card");e.each(u,function(u,a){a=e(a);n=i;i=new t(s,a,u,n,r);o._cards.push(i);if(i.name){o.cards[i.name]=i}if(n){n.next=i}o.modal.find(".wizard-steps .wizard-nav-list").append(i.nav)})},showSubmitCard:function(e){this.log("showing "+e+" submit card");var t=this.el.find(".wizard-"+e);if(t.length){this.hideCards();this.el.find(".wizard-"+e).show()}else{this.log("couldn't find submit card "+e)}},hideSubmitCard:function(e){this.log("hiding "+e+" submit card");this.el.find(".wizard-"+e).hide()},hideSubmitCards:function(){var t=this;e.each(["success","error","failure","loading"],function(e,n){t.hideSubmitCard(n)})},enableNextButton:function(){this.log("enabling next button");this.nextButton.removeAttr("disabled");return this},disableNextButton:function(){this.log("disabling next button");this.nextButton.attr("disabled","disabled");return this},serializeArray:function(){var t=this.form.serializeArray();this.form.find('input[disabled][data-serialize="1"]').each(function(){formObj={name:e(this).attr("name"),value:e(this).val()};t.push(formObj)});return t},serialize:function(){var t=this.form.serialize();this.form.find('input[disabled][data-serialize="1"]').each(function(){t=t+"&"+e(this).attr("name")+"="+e(this).val()});return t},find:function(e){return this.modal.find(e)},submitSuccess:function(){this.log("submit success");this._submitting=false;this.showSubmitCard("success");this.trigger("submitSuccess")},submitFailure:function(){this.log("submit failure");this._submitting=false;this.showSubmitCard("failure");this.trigger("submitFailure")},submitError:function(){this.log("submit error");this._submitting=false;this.showSubmitCard("error");this.trigger("submitError")},_submit:function(){this.log("submitting wizard");this._submitting=true;this.lockCards();this.cancelButton.hide();this.closeButton.hide();this.backButton.hide();this.showSubmitCard("loading");this.updateProgressBar(100);this.changeNextButton(this.args.buttons.submittingText,false);this.disableNextButton();var e=this.trigger("submit");this.trigger("loading")},_onNextClick:function(){this.log("handling 'next' button click");var e=this.getActiveCard();if(this._readyToSubmit&&e.validate()){this._submit()}else{e=this.incrementCard()}},_onBackClick:function(){this.log("handling 'back' button click");var e=this.decrementCard()},_handleNextClick:function(e){var t=e.data;t._onNextClick.call(t)},_handleBackClick:function(e){var t=e.data;t._onBackClick.call(t)},_defaultSubmit:function(t){e.ajax({type:"POST",url:t.args.submitUrl,data:t.serialize(),dataType:"json"}).done(function(e){t.submitSuccess();t.hideButtons();t.updateProgressBar(0)}).fail(function(){t.submitFailure();t.hideButtons()})}}})(window.jQuery) diff --git a/conf/setup_dashboard/cep-config/config-xmls.zip b/conf/setup_dashboard/cep-config/config-xmls.zip new file mode 100644 index 0000000..1aecb4a Binary files /dev/null and b/conf/setup_dashboard/cep-config/config-xmls.zip differ diff --git a/conf/setup_dashboard/cep-config/eventbuilders/TrackerJsonBuilder.xml b/conf/setup_dashboard/cep-config/eventbuilders/TrackerJsonBuilder.xml new file mode 100755 index 0000000..f317088 --- /dev/null +++ b/conf/setup_dashboard/cep-config/eventbuilders/TrackerJsonBuilder.xml @@ -0,0 +1,34 @@ + + + + trackingstream + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/setup_dashboard/cep-config/eventbuilders/geoJsonBuilder.xml b/conf/setup_dashboard/cep-config/eventbuilders/geoJsonBuilder.xml new file mode 100755 index 0000000..2982073 --- /dev/null +++ b/conf/setup_dashboard/cep-config/eventbuilders/geoJsonBuilder.xml @@ -0,0 +1,33 @@ + + + + geoJson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/setup_dashboard/cep-config/eventformatters/geoJsonFormatter.xml b/conf/setup_dashboard/cep-config/eventformatters/geoJsonFormatter.xml new file mode 100755 index 0000000..935944c --- /dev/null +++ b/conf/setup_dashboard/cep-config/eventformatters/geoJsonFormatter.xml @@ -0,0 +1,27 @@ + + + + + + { + "type": "Feature", + "id": "{{id}}", + "properties": { + "speed": {{speed}}, + "heading": {{heading}}, + "state": "{{state}}", + "information": "{{information}}", + "notify": {{notify}} + }, + "geometry": { + "type": "Point", + "coordinates": [{{longitude}},{{latitude}}] + } +} + + + + geoDataEndPoint + + diff --git a/conf/setup_dashboard/cep-config/eventformatters/mysqlFormatter.xml b/conf/setup_dashboard/cep-config/eventformatters/mysqlFormatter.xml new file mode 100755 index 0000000..381ffc5 --- /dev/null +++ b/conf/setup_dashboard/cep-config/eventformatters/mysqlFormatter.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + alerts_history + + insert + + diff --git a/conf/setup_dashboard/cep-config/executionplans/eventsFunionGenerateNotofications.xml b/conf/setup_dashboard/cep-config/executionplans/eventsFunionGenerateNotofications.xml new file mode 100755 index 0000000..0e8d1a7 --- /dev/null +++ b/conf/setup_dashboard/cep-config/executionplans/eventsFunionGenerateNotofications.xml @@ -0,0 +1,27 @@ + + + Fuse multiple events into one event and notify to user if a state has change from the previous value + + 0 + false + + + + + + + + + + diff --git a/conf/setup_dashboard/cep-config/executionplans/geo_speed_alert.xml b/conf/setup_dashboard/cep-config/executionplans/geo_speed_alert.xml new file mode 100755 index 0000000..389199c --- /dev/null +++ b/conf/setup_dashboard/cep-config/executionplans/geo_speed_alert.xml @@ -0,0 +1,20 @@ + + + When an object reatch to a pre-defined aleart speed, CEP change the state of that event to ALEARTED and overwrite the information. + + 0 + false + + + + + = 34]#transform.geo:subscribeExecutionPlan() +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "ALERTED" as state, "This vehicle is going over speed!!" as information +insert into dataOut; +from dataIn[speed < 34] +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "NORMAL" as state, "Normal driving pattern" as information +insert into dataOut;]]> + + + + diff --git a/conf/setup_dashboard/cep-config/executionplans/geo_testing2_proxymity_alert.xml b/conf/setup_dashboard/cep-config/executionplans/geo_testing2_proxymity_alert.xml new file mode 100755 index 0000000..620eabb --- /dev/null +++ b/conf/setup_dashboard/cep-config/executionplans/geo_testing2_proxymity_alert.xml @@ -0,0 +1,26 @@ + + + + + 0 + false + + + + + + + + + diff --git a/conf/setup_dashboard/cep-config/executionplans/geo_within_Akuregoda_alert.xml b/conf/setup_dashboard/cep-config/executionplans/geo_within_Akuregoda_alert.xml new file mode 100755 index 0000000..5894e7b --- /dev/null +++ b/conf/setup_dashboard/cep-config/executionplans/geo_within_Akuregoda_alert.xml @@ -0,0 +1,21 @@ + + + + + 0 + false + + + + + from dataIn[geo:iswithin(longitude,latitude,"{'type':'Polygon','coordinates':[[[79.9320602416992,6.878369189928266],[79.9148941040039,6.894729791048567],[79.92382049560545,6.911430655357409],[79.95574951171875,6.912793965187959],[79.96261596679686,6.89984236311519],[79.96330261230467,6.879391744022743],[79.9320602416992,6.878369189928266]]]}")==true]#transform.geo:subscribeExecutionPlan() +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "ALERTED" as state, "This vehicle is in Akuregoda1 restricted area!!!" as information +insert into dataOut; +from dataIn[geo:iswithin(longitude,latitude,"{'type':'Polygon','coordinates':[[[79.9320602416992,6.878369189928266],[79.9148941040039,6.894729791048567],[79.92382049560545,6.911430655357409],[79.95574951171875,6.912793965187959],[79.96261596679686,6.89984236311519],[79.96330261230467,6.879391744022743],[79.9320602416992,6.878369189928266]]]}")!=true] +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "NORMAL" as state, "" as information +insert into dataOut; + + + + diff --git a/conf/setup_dashboard/cep-config/executionplans/geo_within_Colombo_alert.xml b/conf/setup_dashboard/cep-config/executionplans/geo_within_Colombo_alert.xml new file mode 100755 index 0000000..02f1bc4 --- /dev/null +++ b/conf/setup_dashboard/cep-config/executionplans/geo_within_Colombo_alert.xml @@ -0,0 +1,21 @@ + + + + + 0 + false + + + + + from dataIn[geo:iswithin(longitude,latitude,"{'type':'Polygon','coordinates':[[[79.85206604003906,6.928982968564734],[79.85584259033203,6.943467395179309],[79.86974716186523,6.941592965070489],[79.88245010375977,6.92506357659203],[79.87335205078125,6.919440044277738],[79.85206604003906,6.928982968564734]]]}")==true]#transform.geo:subscribeExecutionPlan() +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "ALERTED" as state, "This vehicle is in Fence1 restricted area!!!" as information +insert into dataOut; +from dataIn[geo:iswithin(longitude,latitude,"{'type':'Polygon','coordinates':[[[79.85206604003906,6.928982968564734],[79.85584259033203,6.943467395179309],[79.86974716186523,6.941592965070489],[79.88245010375977,6.92506357659203],[79.87335205078125,6.919440044277738],[79.85206604003906,6.928982968564734]]]}")!=true] +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "NORMAL" as state, "" as information +insert into dataOut; + + + + diff --git a/conf/setup_dashboard/cep-config/executionplans/geo_within_Havlock_town_alert.xml b/conf/setup_dashboard/cep-config/executionplans/geo_within_Havlock_town_alert.xml new file mode 100755 index 0000000..4ceefc0 --- /dev/null +++ b/conf/setup_dashboard/cep-config/executionplans/geo_within_Havlock_town_alert.xml @@ -0,0 +1,21 @@ + + + + + 0 + false + + + + + from dataIn[geo:iswithin(longitude,latitude,"{'type':'Polygon','coordinates':[[[79.8676872253418,6.889276320041728],[79.85858917236328,6.899671944935481],[79.86236572265625,6.909556097920147],[79.87661361694336,6.909896927097974],[79.88004684448242,6.902057793869],[79.88056182861328,6.894218530807201],[79.87232208251953,6.891150957776738],[79.8676872253418,6.889276320041728]]]}")==true]#transform.geo:subscribeExecutionPlan() +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "ALERTED" as state, "This vehicle is in Havlock_town1 restricted area!!!" as information +insert into dataOut; +from dataIn[geo:iswithin(longitude,latitude,"{'type':'Polygon','coordinates':[[[79.8676872253418,6.889276320041728],[79.85858917236328,6.899671944935481],[79.86236572265625,6.909556097920147],[79.87661361694336,6.909896927097974],[79.88004684448242,6.902057793869],[79.88056182861328,6.894218530807201],[79.87232208251953,6.891150957776738],[79.8676872253418,6.889276320041728]]]}")!=true] +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "NORMAL" as state, "" as information +insert into dataOut; + + + + diff --git a/conf/setup_dashboard/cep-config/executionplans/geo_within_marine_drive_alert.xml b/conf/setup_dashboard/cep-config/executionplans/geo_within_marine_drive_alert.xml new file mode 100755 index 0000000..b26bcf2 --- /dev/null +++ b/conf/setup_dashboard/cep-config/executionplans/geo_within_marine_drive_alert.xml @@ -0,0 +1,21 @@ + + + + + 0 + false + + + + + from dataIn[geo:iswithin(longitude,latitude,"{'type':'Polygon','coordinates':[[[79.85635757446289,6.878880467250712],[79.84176635742188,6.931027855791152],[79.84502792358398,6.931879889517218],[79.86202239990233,6.875471941373295],[79.86425399780273,6.868313957458993],[79.85893249511719,6.867973098394459],[79.85635757446289,6.878880467250712]]]}")==true]#transform.geo:subscribeExecutionPlan() +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "ALERTED" as state, "This vehicle is in marine_drive1 restricted area!!!" as information +insert into dataOut; +from dataIn[geo:iswithin(longitude,latitude,"{'type':'Polygon','coordinates':[[[79.85635757446289,6.878880467250712],[79.84176635742188,6.931027855791152],[79.84502792358398,6.931879889517218],[79.86202239990233,6.875471941373295],[79.86425399780273,6.868313957458993],[79.85893249511719,6.867973098394459],[79.85635757446289,6.878880467250712]]]}")!=true] +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "NORMAL" as state, "" as information +insert into dataOut; + + + + diff --git a/conf/setup_dashboard/cep-config/executionplans/geo_within_town_hall_alert.xml b/conf/setup_dashboard/cep-config/executionplans/geo_within_town_hall_alert.xml new file mode 100755 index 0000000..3162e47 --- /dev/null +++ b/conf/setup_dashboard/cep-config/executionplans/geo_within_town_hall_alert.xml @@ -0,0 +1,21 @@ + + + + + 0 + false + + + + + from dataIn[geo:iswithin(longitude,latitude,"{'type':'Polygon','coordinates':[[[79.86013412475586,6.910578584716491],[79.8544692993164,6.916543046940773],[79.86391067504883,6.923018663504],[79.87112045288086,6.918417576658827],[79.87112045288086,6.915179747927456],[79.86682891845703,6.909896927097974],[79.86013412475586,6.910578584716491]]]}")==true]#transform.geo:subscribeExecutionPlan() +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "ALERTED" as state, "This vehicle is in town_hall1 restricted area!!!" as information +insert into dataOut; +from dataIn[geo:iswithin(longitude,latitude,"{'type':'Polygon','coordinates':[[[79.86013412475586,6.910578584716491],[79.8544692993164,6.916543046940773],[79.86391067504883,6.923018663504],[79.87112045288086,6.918417576658827],[79.87112045288086,6.915179747927456],[79.86682891845703,6.909896927097974],[79.86013412475586,6.910578584716491]]]}")!=true] +select id , latitude, longitude,timeStamp, speed, heading ,eventId , "NORMAL" as state, "" as information +insert into dataOut; + + + + diff --git a/conf/setup_dashboard/cep-config/executionplans/inputStandardizer.xml b/conf/setup_dashboard/cep-config/executionplans/inputStandardizer.xml new file mode 100755 index 0000000..976c723 --- /dev/null +++ b/conf/setup_dashboard/cep-config/executionplans/inputStandardizer.xml @@ -0,0 +1,18 @@ + + + + + 0 + false + + + + + + + + + diff --git a/conf/setup_dashboard/cep-config/inputeventadaptors/GPSStreamAdaptor.xml b/conf/setup_dashboard/cep-config/inputeventadaptors/GPSStreamAdaptor.xml new file mode 100755 index 0000000..fdc5a5d --- /dev/null +++ b/conf/setup_dashboard/cep-config/inputeventadaptors/GPSStreamAdaptor.xml @@ -0,0 +1,3 @@ + + diff --git a/conf/setup_dashboard/cep-config/outputeventadaptors/DefaultMysqlOutputAdaptor.xml b/conf/setup_dashboard/cep-config/outputeventadaptors/DefaultMysqlOutputAdaptor.xml new file mode 100755 index 0000000..19bfa96 --- /dev/null +++ b/conf/setup_dashboard/cep-config/outputeventadaptors/DefaultMysqlOutputAdaptor.xml @@ -0,0 +1,5 @@ + + + mysql_db + diff --git a/conf/setup_dashboard/cep-config/outputeventadaptors/DefaultWebsocketOutputAdaptor.xml b/conf/setup_dashboard/cep-config/outputeventadaptors/DefaultWebsocketOutputAdaptor.xml new file mode 100755 index 0000000..c8f5574 --- /dev/null +++ b/conf/setup_dashboard/cep-config/outputeventadaptors/DefaultWebsocketOutputAdaptor.xml @@ -0,0 +1,3 @@ + + diff --git a/conf/setup_dashboard/cep-config/streamdefinitions.zip b/conf/setup_dashboard/cep-config/streamdefinitions.zip new file mode 100644 index 0000000..8907359 Binary files /dev/null and b/conf/setup_dashboard/cep-config/streamdefinitions.zip differ diff --git a/conf/setup_dashboard/cep-config/streamdefinitions/AlertsNotifications:1.0.0.json b/conf/setup_dashboard/cep-config/streamdefinitions/AlertsNotifications:1.0.0.json new file mode 100644 index 0000000..d6ebc5f --- /dev/null +++ b/conf/setup_dashboard/cep-config/streamdefinitions/AlertsNotifications:1.0.0.json @@ -0,0 +1,33 @@ +{ + "streamId": "AlertsNotifications:1.0.0", + "name": "AlertsNotifications", + "version": "1.0.0", + "nickName": "", + "description": "Alerts which need to be notified to the user, output only when the alert information is changed.", + "payloadData": [ + { + "name": "id", + "type": "STRING" + }, + { + "name": "state", + "type": "STRING" + }, + { + "name": "information", + "type": "STRING" + }, + { + "name": "timeStamp", + "type": "STRING" + }, + { + "name": "latitude", + "type": "DOUBLE" + }, + { + "name": "longitude", + "type": "DOUBLE" + } + ] +} \ No newline at end of file diff --git a/conf/setup_dashboard/cep-config/streamdefinitions/fusedSpatialEvent:1.0.0.json b/conf/setup_dashboard/cep-config/streamdefinitions/fusedSpatialEvent:1.0.0.json new file mode 100644 index 0000000..b751367 --- /dev/null +++ b/conf/setup_dashboard/cep-config/streamdefinitions/fusedSpatialEvent:1.0.0.json @@ -0,0 +1,45 @@ +{ + "streamId": "fusedSpatialEvent:1.0.0", + "name": "fusedSpatialEvent", + "version": "1.0.0", + "nickName": "", + "description": "", + "payloadData": [ + { + "name": "id", + "type": "STRING" + }, + { + "name": "latitude", + "type": "DOUBLE" + }, + { + "name": "longitude", + "type": "DOUBLE" + }, + { + "name": "timeStamp", + "type": "STRING" + }, + { + "name": "speed", + "type": "FLOAT" + }, + { + "name": "heading", + "type": "FLOAT" + }, + { + "name": "state", + "type": "STRING" + }, + { + "name": "information", + "type": "STRING" + }, + { + "name": "notify", + "type": "BOOL" + } + ] +} \ No newline at end of file diff --git a/conf/setup_dashboard/cep-config/streamdefinitions/processedSpatialEvents:1.0.0.json b/conf/setup_dashboard/cep-config/streamdefinitions/processedSpatialEvents:1.0.0.json new file mode 100644 index 0000000..6533374 --- /dev/null +++ b/conf/setup_dashboard/cep-config/streamdefinitions/processedSpatialEvents:1.0.0.json @@ -0,0 +1,45 @@ +{ + "streamId": "processedSpatialEvents:1.0.0", + "name": "processedSpatialEvents", + "version": "1.0.0", + "nickName": "", + "description": "", + "payloadData": [ + { + "name": "id", + "type": "STRING" + }, + { + "name": "latitude", + "type": "DOUBLE" + }, + { + "name": "longitude", + "type": "DOUBLE" + }, + { + "name": "timeStamp", + "type": "STRING" + }, + { + "name": "speed", + "type": "FLOAT" + }, + { + "name": "heading", + "type": "FLOAT" + }, + { + "name": "eventId", + "type": "STRING" + }, + { + "name": "state", + "type": "STRING" + }, + { + "name": "information", + "type": "STRING" + } + ] +} \ No newline at end of file diff --git a/conf/setup_dashboard/cep-config/streamdefinitions/rawInputStream:1.0.0.json b/conf/setup_dashboard/cep-config/streamdefinitions/rawInputStream:1.0.0.json new file mode 100644 index 0000000..032ad56 --- /dev/null +++ b/conf/setup_dashboard/cep-config/streamdefinitions/rawInputStream:1.0.0.json @@ -0,0 +1,33 @@ +{ + "streamId": "rawInputStream:1.0.0", + "name": "rawInputStream", + "version": "1.0.0", + "nickName": "", + "description": "Any raw data send from external users to CEP, this stream will be used by inputStandardizer to transform raw events to standard spatial event(with spatial standards ).", + "payloadData": [ + { + "name": "id", + "type": "STRING" + }, + { + "name": "timeStamp", + "type": "STRING" + }, + { + "name": "latitude", + "type": "DOUBLE" + }, + { + "name": "longitude", + "type": "DOUBLE" + }, + { + "name": "speed", + "type": "FLOAT" + }, + { + "name": "heading", + "type": "FLOAT" + } + ] +} \ No newline at end of file diff --git a/conf/setup_dashboard/cep-config/streamdefinitions/standardSpatialEvents:1.0.0.json b/conf/setup_dashboard/cep-config/streamdefinitions/standardSpatialEvents:1.0.0.json new file mode 100644 index 0000000..a719e0c --- /dev/null +++ b/conf/setup_dashboard/cep-config/streamdefinitions/standardSpatialEvents:1.0.0.json @@ -0,0 +1,37 @@ +{ + "streamId": "standardSpatialEvents:1.0.0", + "name": "standardSpatialEvents", + "version": "1.0.0", + "nickName": "", + "description": "Standardized event by spatial InputStandardizer execution plan", + "payloadData": [ + { + "name": "id", + "type": "STRING" + }, + { + "name": "latitude", + "type": "DOUBLE" + }, + { + "name": "longitude", + "type": "DOUBLE" + }, + { + "name": "timeStamp", + "type": "STRING" + }, + { + "name": "speed", + "type": "FLOAT" + }, + { + "name": "heading", + "type": "FLOAT" + }, + { + "name": "eventId", + "type": "STRING" + } + ] +} \ No newline at end of file diff --git a/conf/setup_dashboard/mysql-config/geo_dashboard_db_struct.sql b/conf/setup_dashboard/mysql-config/geo_dashboard_db_struct.sql new file mode 100755 index 0000000..14241ab --- /dev/null +++ b/conf/setup_dashboard/mysql-config/geo_dashboard_db_struct.sql @@ -0,0 +1,36 @@ +-- DROP DATABASE IF EXISTS wso2_geo; +-- CREATE DATABASE wso2_geo DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci; +-- USE wso2_geo; + +-- Create syntax for TABLE 'alerts_history' +CREATE TABLE `alerts_history` ( + `id` varchar (255) not null , + `state` varchar (100), + `timeStamp` varchar (255), + `information` text, + `longitude` float, + `latitude` float, + primary key (`id`,`timeStamp`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +-- Create syntax for TABLE 'webMapService' +CREATE TABLE `webMapService` ( + `id` INT NOT NULL AUTO_INCREMENT , + `serviceUrl` MEDIUMTEXT NOT NULL, + `name` VARCHAR (255), + `layers` VARCHAR (255), + `version` VARCHAR (255), + `format` VARCHAR (255), + KEY `id` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +-- Create syntax for TABLE 'tileServers' +CREATE TABLE `tileServers` ( + `id` INT NOT NULL AUTO_INCREMENT , + `url` MEDIUMTEXT NOT NULL, + `name` VARCHAR (255), + `subdomains` VARCHAR (255), + `attribution` MEDIUMTEXT , + `maxzoom` INT , + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; diff --git a/conf/setup_dashboard/mysql-config/geo_db_setup.sh b/conf/setup_dashboard/mysql-config/geo_db_setup.sh new file mode 100755 index 0000000..613c3b6 --- /dev/null +++ b/conf/setup_dashboard/mysql-config/geo_db_setup.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash + +# Default values for database variables. +dbhost="localhost" +dbport=3306 +dbname="wso2_geo" +dir=$( cd "$( dirname "$0" )" && pwd ) + +logo() { + echo "================================================================================================" + echo " W S O 2 G E O D A T A S E T U P " + echo "================================================================================================" +} + +usage() { + logo + echo " Parameters indicates the following information:" + echo " -u User name to access database server." + echo " -p User password to access database server." + echo " -h Data Base Server address (default: localhost)." + echo " -r Data Base Server Port (default: 3306)" + echo " -n Data Base Name for the geonames.org data (default: wso2_geo)" + echo "================================================================================================" + exit -1 +} + +if [ $# -lt 1 ]; then + usage + exit 1 +fi + +logo + +# Parses command line parameters. +while getopts "u:p:h:r:n:" opt; +do + case $opt in + u) dbusername=$OPTARG ;; + p) dbpassword=$OPTARG ;; + h) dbhost=$OPTARG ;; + r) dbport=$OPTARG ;; + n) dbname=$OPTARG ;; + esac +done + +if [ -z $dbusername ]; then + echo "No user name provided for accessing the database. Please write some value in parameter -u..." + exit 1 +fi + +if [ -z $dbpassword ]; then + echo "No user password provided for accessing the database. Please write some value in parameter -p..." + exit 1 +fi + +echo "Database parameters being used..." +echo "UserName: " $dbusername +echo "Password: " $dbpassword +echo "DB Host: " $dbhost +echo "DB Port: " $dbport +echo "DB Name: " $dbname + + +echo "Creating database $dbname..." +mysql -h $dbhost -P $dbport -u $dbusername -p$dbpassword -Bse "DROP DATABASE IF EXISTS $dbname;" +mysql -h $dbhost -P $dbport -u $dbusername -p$dbpassword -Bse "CREATE DATABASE $dbname DEFAULT CHARACTER SET utf8;" + +echo "Creating tables for database $dbname..." +mysql -h $dbhost -P $dbport -u $dbusername -p$dbpassword -Bse "USE $dbname;" +mysql -h $dbhost -P $dbport -u $dbusername -p$dbpassword $dbname < $dir/geo_dashboard_db_struct.sql + + +if [ $? == 0 ]; then + echo "[OK]" +else + echo "[FAILED]" +fi + +exit 0 diff --git a/conf/setup_dashboard/mysql-config/mysql-config.zip b/conf/setup_dashboard/mysql-config/mysql-config.zip new file mode 100644 index 0000000..ef7dca7 Binary files /dev/null and b/conf/setup_dashboard/mysql-config/mysql-config.zip differ diff --git a/controllers/modals/setup_dashboard.jag b/controllers/modals/setup_dashboard.jag new file mode 100644 index 0000000..35b0fba --- /dev/null +++ b/controllers/modals/setup_dashboard.jag @@ -0,0 +1,122 @@ +

    Setup Geo-Dashboard

    + +
    +

    *CEP info

    +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    +

    + * Require > CEP 4.0 or CEP with websocket output adapter component. +

    +
    + +
    + +
    +

    Mysql conf

    + +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    +

    Download MySql + Configuration and run geo_db_setup.sh with sudo privilages.(Get JDBC Driver if required.)

    +
    + +
    + +
    +

    CEP queries

    + +
    +

    + Download CEP configuration files + (eventbuilders,eventformatters,executionplans,inputeventadaptors,outputeventadaptors) and + copy into
    {path_to_cep}/repository/deployment/server/ directory. +

    +
    +
    +
    +

    + Download CEP stream definitions and + append to
    {path_to_cep}/repository/conf/data-bridge/stream-definitions.xml file. +

    +
    +
    + + +
    +

    CEP Ext's

    +
    +

    + Get (Clone) a copy of siddhi geo extensions from github and build the necessary extensions and put .jar(s)> into +
    {path_to_cep}/repository/components/lib/ directory. +

    +
    +
    + +
    +

    + Add the following fully-qualified class name(s) to +
    {path_to_cep}/repository/conf/siddhi/siddhi.extension file.
    + + org.wso2.cep.geo.notify.NotifyAlert
    + org.wso2.cep.geo.functions.EventIdGenerator
    + org.wso2.cep.geo.libs.FuseEvents
    + org.wso2.cep.geo.libs.executionPlanSubscriber
    + org.wso2.cep.geo.proximity.GeoProximity
    + org.wso2.cep.geo.GeoIsWithin +
    +

    +
    + +
    + +
    +

    Update jaggery

    + If you get the following error when trying to login, Most probably it is cause by using outdated jaggery server. + + Copy this directory to your local
    {path_to_cep}/modules/carbon/scripts/server/ directory in Jaggery server. +
    \ No newline at end of file diff --git a/controllers/setup_dashboard.jag b/controllers/setup_dashboard.jag new file mode 100644 index 0000000..9d888a4 --- /dev/null +++ b/controllers/setup_dashboard.jag @@ -0,0 +1,9 @@ +<% + +var log = new Log(); +log.info(request.getAllParameters("UTF-8")); +response.contentType = 'application/json'; +response.characterEncoding = 'UTF-8'; +response.content = true; + +%> \ No newline at end of file diff --git a/index.jag b/index.jag index 062fabb..b50dc0a 100755 --- a/index.jag +++ b/index.jag @@ -15,11 +15,27 @@ + + + + + + + + @@ -73,7 +89,7 @@
    - +
    @@ -87,9 +103,144 @@ +
    + + + + +
    + +

    Setup Geo-Dashboard

    + +
    +

    *CEP info

    +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    +

    + * Require > CEP 4.0 or CEP with websocket output adapter component. +

    +
    + +
    + +
    +

    Mysql conf

    + +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    +

    Download MySql + Configuration and run geo_db_setup.sh with sudo privilages.(Get JDBC Driver if required.)

    +
    + +
    + +
    +

    CEP queries

    + +
    +

    + Download CEP configuration files + (eventbuilders,eventformatters,executionplans,inputeventadaptors,outputeventadaptors) and + copy into
    {path_to_cep}/repository/deployment/server/ directory. +

    +
    +
    +
    +

    + Download CEP stream definitions and + append to
    {path_to_cep}/repository/conf/data-bridge/stream-definitions.xml file. +

    +
    +
    + + +
    +

    CEP Ext's

    +
    +

    + Get (Clone) a copy of siddhi geo extensions from github and build the necessary extensions and put .jar(s)> into +
    {path_to_cep}/repository/components/lib/ directory. +

    +
    +
    + +
    +

    + Add the following fully-qualified class name(s) to +
    {path_to_cep}/repository/conf/siddhi/siddhi.extension file.
    + + org.wso2.cep.geo.notify.NotifyAlert
    + org.wso2.cep.geo.functions.EventIdGenerator
    + org.wso2.cep.geo.libs.FuseEvents
    + org.wso2.cep.geo.libs.executionPlanSubscriber
    + org.wso2.cep.geo.proximity.GeoProximity
    + org.wso2.cep.geo.GeoIsWithin +
    +

    +
    + +
    + +
    +

    Update jaggery

    + If you get the following error when trying to login, Most probably it is cause by using outdated jaggery server. + + Copy this directory to your local
    {path_to_cep}/modules/carbon/scripts/server/ directory in Jaggery server. +
    +
    + +
    diff --git a/map.jag b/map.jag index e0900ca..922634e 100644 --- a/map.jag +++ b/map.jag @@ -1,13 +1,13 @@ <% (function (){ %> <% if(!session.get("user")){ - session.put("error", "Please login using tenant details before using map"); - try{ - response.sendRedirect("/geo_dashboard/new/"); - return; - } catch(error){ - return; - } +session.put("error", "Please login using tenant details before using map"); +try{ +response.sendRedirect("/geo_dashboard/new/"); +return; +} catch(error){ +return; +} } var user = session.get("user"); @@ -44,6 +44,10 @@ var user = session.get("user"); + + + + @@ -51,6 +55,9 @@ var user = session.get("user"); + + + @@ -79,7 +86,7 @@ var user = session.get("user"); @@ -137,11 +144,21 @@ var user = session.get("user"); color: firebrick; } - .sectionJointStyle{ + .sectionJointStyle { stroke-dasharray: 3, 20; } + + @@ -254,12 +271,14 @@ var user = session.get("user"); @@ -270,8 +289,10 @@ var user = session.get("user"); @@ -291,13 +312,10 @@ var user = session.get("user"); System Administrtion - - - - - - - +
  • + Setup + Dashboard +
  • @@ -775,6 +793,134 @@ var user = session.get("user");
    + + +
    + +

    Setup Geo-Dashboard

    + +
    +

    *CEP info

    +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    +

    + * Require > CEP 4.0 or CEP with websocket output adapter component. +

    +
    + +
    + +
    +

    Mysql conf

    + +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    +

    Download MySql + Configuration and run geo_db_setup.sh with sudo privilages.(Get JDBC Driver if required.)

    +
    + +
    + +
    +

    CEP queries

    + +
    +

    + Download CEP configuration files + (eventbuilders,eventformatters,executionplans,inputeventadaptors,outputeventadaptors) and + copy into
    {path_to_cep}/repository/deployment/server/ directory. +

    +
    +
    +
    +

    + Download CEP stream definitions and + append to
    {path_to_cep}/repository/conf/data-bridge/stream-definitions.xml file. +

    +
    +
    + + +
    +

    CEP Ext's

    +
    +

    + Get (Clone) a copy of siddhi geo extensions from github and build the necessary extensions and put .jar(s)> into +
    {path_to_cep}/repository/components/lib/ directory. +

    +
    +
    + +
    +

    + Add the following fully-qualified class name(s) to +
    {path_to_cep}/repository/conf/siddhi/siddhi.extension file.
    + + org.wso2.cep.geo.notify.NotifyAlert
    + org.wso2.cep.geo.functions.EventIdGenerator
    + org.wso2.cep.geo.libs.FuseEvents
    + org.wso2.cep.geo.libs.executionPlanSubscriber
    + org.wso2.cep.geo.proximity.GeoProximity
    + org.wso2.cep.geo.GeoIsWithin +
    +

    +
    + +
    + +
    +

    Update jaggery

    + If you get the following error when trying to login, Most probably it is cause by using outdated jaggery server. + + Copy this directory to your local
    {path_to_cep}/modules/carbon/scripts/server/ directory in Jaggery server. +
    +
    + <% })();%> \ No newline at end of file