commit 93770f297a1ccfbe32b15abb012415a30d0de9e5 Author: exodus4d Date: Sun Oct 26 21:51:04 2014 +0100 project structure diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..412eeda7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4ed52f2c --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# ======================== +# project files +# ======================== +.sass-cache +.build_js +.css diff --git a/config.rb b/config.rb new file mode 100644 index 00000000..76936db3 --- /dev/null +++ b/config.rb @@ -0,0 +1,28 @@ +require 'compass/import-once/activate' +require 'bootstrap-sass' +require 'compass/import-once/activate' +# Require any additional compass plugins here. + + +http_path = "/" +css_dir = "css" +sass_dir = "sass" +images_dir = "images" +javascripts_dir = "javascripts" + +# You can select your preferred output style here (can be overridden via the command line): +# output_style = :expanded or :nested or :compact or :compressed +output_style = :compressed + +# To enable relative paths to assets via compass helper functions. Uncomment: +# relative_assets = true + +# To disable debugging comments that display the original location of your selectors. Uncomment: +line_comments = false + + +# If you prefer the indented syntax, you might want to regenerate this +# project again passing --syntax sass, or you can uncomment this: +# preferred_syntax = :sass +# and then run: +# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass diff --git a/fonts/FontAwesome.otf b/fonts/FontAwesome.otf new file mode 100644 index 00000000..3461e3fc Binary files /dev/null and b/fonts/FontAwesome.otf differ diff --git a/fonts/Oxygen-Bold.ttf b/fonts/Oxygen-Bold.ttf new file mode 100644 index 00000000..835ab053 Binary files /dev/null and b/fonts/Oxygen-Bold.ttf differ diff --git a/fonts/Oxygen-Light.ttf b/fonts/Oxygen-Light.ttf new file mode 100644 index 00000000..08b9fec3 Binary files /dev/null and b/fonts/Oxygen-Light.ttf differ diff --git a/fonts/Oxygen-Regular.ttf b/fonts/Oxygen-Regular.ttf new file mode 100644 index 00000000..a66ddf1c Binary files /dev/null and b/fonts/Oxygen-Regular.ttf differ diff --git a/fonts/fontawesome-webfont.eot b/fonts/fontawesome-webfont.eot new file mode 100644 index 00000000..6cfd5660 Binary files /dev/null and b/fonts/fontawesome-webfont.eot differ diff --git a/fonts/fontawesome-webfont.svg b/fonts/fontawesome-webfont.svg new file mode 100644 index 00000000..a9f84695 --- /dev/null +++ b/fonts/fontawesome-webfont.svg @@ -0,0 +1,504 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/fontawesome-webfont.ttf b/fonts/fontawesome-webfont.ttf new file mode 100644 index 00000000..5cd6cff6 Binary files /dev/null and b/fonts/fontawesome-webfont.ttf differ diff --git a/fonts/fontawesome-webfont.woff b/fonts/fontawesome-webfont.woff new file mode 100644 index 00000000..9eaecb37 Binary files /dev/null and b/fonts/fontawesome-webfont.woff differ diff --git a/fonts/glyphicons-halflings-regular.eot b/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 00000000..4a4ca865 Binary files /dev/null and b/fonts/glyphicons-halflings-regular.eot differ diff --git a/fonts/glyphicons-halflings-regular.svg b/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 00000000..e3e2dc73 --- /dev/null +++ b/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/glyphicons-halflings-regular.ttf b/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 00000000..67fa00bf Binary files /dev/null and b/fonts/glyphicons-halflings-regular.ttf differ diff --git a/fonts/glyphicons-halflings-regular.woff b/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 00000000..8c54182a Binary files /dev/null and b/fonts/glyphicons-halflings-regular.woff differ diff --git a/img/jqueryui/ui-bg_flat_0_999999_40x100.png b/img/jqueryui/ui-bg_flat_0_999999_40x100.png new file mode 100644 index 00000000..13c79e86 Binary files /dev/null and b/img/jqueryui/ui-bg_flat_0_999999_40x100.png differ diff --git a/img/jqueryui/ui-bg_flat_0_aaaaaa_40x100.png b/img/jqueryui/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 00000000..e425e6e4 Binary files /dev/null and b/img/jqueryui/ui-bg_flat_0_aaaaaa_40x100.png differ diff --git a/img/jqueryui/ui-bg_glass_55_fbf9ee_1x400.png b/img/jqueryui/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 00000000..3b2914a2 Binary files /dev/null and b/img/jqueryui/ui-bg_glass_55_fbf9ee_1x400.png differ diff --git a/img/jqueryui/ui-bg_glass_65_ffffff_1x400.png b/img/jqueryui/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 00000000..e81a6d06 Binary files /dev/null and b/img/jqueryui/ui-bg_glass_65_ffffff_1x400.png differ diff --git a/img/jqueryui/ui-bg_glass_75_dadada_1x400.png b/img/jqueryui/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 00000000..5a46b47c Binary files /dev/null and b/img/jqueryui/ui-bg_glass_75_dadada_1x400.png differ diff --git a/img/jqueryui/ui-bg_glass_75_e6e6e6_1x400.png b/img/jqueryui/ui-bg_glass_75_e6e6e6_1x400.png new file mode 100644 index 00000000..86c2baa6 Binary files /dev/null and b/img/jqueryui/ui-bg_glass_75_e6e6e6_1x400.png differ diff --git a/img/jqueryui/ui-bg_glass_75_ffffff_1x400.png b/img/jqueryui/ui-bg_glass_75_ffffff_1x400.png new file mode 100644 index 00000000..52ff2d68 Binary files /dev/null and b/img/jqueryui/ui-bg_glass_75_ffffff_1x400.png differ diff --git a/img/jqueryui/ui-bg_highlight-soft_75_cccccc_1x100.png b/img/jqueryui/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 00000000..3cd467e1 Binary files /dev/null and b/img/jqueryui/ui-bg_highlight-soft_75_cccccc_1x100.png differ diff --git a/img/jqueryui/ui-bg_inset-soft_95_fef1ec_1x100.png b/img/jqueryui/ui-bg_inset-soft_95_fef1ec_1x100.png new file mode 100644 index 00000000..a50a17f9 Binary files /dev/null and b/img/jqueryui/ui-bg_inset-soft_95_fef1ec_1x100.png differ diff --git a/img/jqueryui/ui-icons_222222_256x240.png b/img/jqueryui/ui-icons_222222_256x240.png new file mode 100644 index 00000000..0085d125 Binary files /dev/null and b/img/jqueryui/ui-icons_222222_256x240.png differ diff --git a/img/jqueryui/ui-icons_2e83ff_256x240.png b/img/jqueryui/ui-icons_2e83ff_256x240.png new file mode 100644 index 00000000..3bb63f15 Binary files /dev/null and b/img/jqueryui/ui-icons_2e83ff_256x240.png differ diff --git a/img/jqueryui/ui-icons_428bca_256x240.png b/img/jqueryui/ui-icons_428bca_256x240.png new file mode 100644 index 00000000..5beba3aa Binary files /dev/null and b/img/jqueryui/ui-icons_428bca_256x240.png differ diff --git a/img/jqueryui/ui-icons_454545_256x240.png b/img/jqueryui/ui-icons_454545_256x240.png new file mode 100644 index 00000000..a813d3e3 Binary files /dev/null and b/img/jqueryui/ui-icons_454545_256x240.png differ diff --git a/img/jqueryui/ui-icons_555555_256x240.png b/img/jqueryui/ui-icons_555555_256x240.png new file mode 100644 index 00000000..5dcdcb41 Binary files /dev/null and b/img/jqueryui/ui-icons_555555_256x240.png differ diff --git a/img/jqueryui/ui-icons_888888_256x240.png b/img/jqueryui/ui-icons_888888_256x240.png new file mode 100644 index 00000000..0b5ccc85 Binary files /dev/null and b/img/jqueryui/ui-icons_888888_256x240.png differ diff --git a/img/jqueryui/ui-icons_999999_256x240.png b/img/jqueryui/ui-icons_999999_256x240.png new file mode 100644 index 00000000..da7e727b Binary files /dev/null and b/img/jqueryui/ui-icons_999999_256x240.png differ diff --git a/img/jqueryui/ui-icons_cd0a0a_256x240.png b/img/jqueryui/ui-icons_cd0a0a_256x240.png new file mode 100644 index 00000000..8c3af0c4 Binary files /dev/null and b/img/jqueryui/ui-icons_cd0a0a_256x240.png differ diff --git a/img/jqueryui/ui-icons_f0ad4e_256x240.png b/img/jqueryui/ui-icons_f0ad4e_256x240.png new file mode 100644 index 00000000..fabf7cf3 Binary files /dev/null and b/img/jqueryui/ui-icons_f0ad4e_256x240.png differ diff --git a/img/jqueryui/ui-icons_f6cf3b_256x240.png b/img/jqueryui/ui-icons_f6cf3b_256x240.png new file mode 100644 index 00000000..3c357457 Binary files /dev/null and b/img/jqueryui/ui-icons_f6cf3b_256x240.png differ diff --git a/img/jqueryui/ui-icons_ffffff_256x240.png b/img/jqueryui/ui-icons_ffffff_256x240.png new file mode 100644 index 00000000..42f8f992 Binary files /dev/null and b/img/jqueryui/ui-icons_ffffff_256x240.png differ diff --git a/img/mybg.png b/img/mybg.png new file mode 100644 index 00000000..e6569aaa Binary files /dev/null and b/img/mybg.png differ diff --git a/index.htm b/index.htm new file mode 100644 index 00000000..901f07dc --- /dev/null +++ b/index.htm @@ -0,0 +1,21 @@ + + + + + + + Pathfinder - mapping tool + + + + + + + + + + +
+
+ + diff --git a/js/app.js b/js/app.js new file mode 100644 index 00000000..ac229a67 --- /dev/null +++ b/js/app.js @@ -0,0 +1,28 @@ + +requirejs.config({ + "baseUrl": "js", // user build_js files, change to "js" for un-compressed source + "paths": { + //"lib": "lib", + // "app": "app", + "layout": "layout", + "jquery": "lib/jquery-1.11.1.min", + //"jquery": "lib/jquery-2.1.1.min", + "jqueryUI": "lib/jquery-ui.min", + "bootstrap": "lib/bootstrap", + "text": "lib/requirejs/text", + "templates": "../templates", + "jsPlumb": "lib/jsPlumb-1.6.4-min" + }, + shim: { + "bootstrap": { + deps: ["jquery"] + }, + "jqueryUI": { + export:"$", + deps: ["jquery"] + } + } +}); + +// Load the main app module to start the app +requirejs(["app/main"]); diff --git a/js/app/ccp.js b/js/app/ccp.js new file mode 100644 index 00000000..c6bb7429 --- /dev/null +++ b/js/app/ccp.js @@ -0,0 +1,57 @@ +/** + * Global CCPEvE function wrapper + */ + +define(["jquery", "app/render", "app/init"], function($, Render, Config) { + + "use strict"; + + /** + * in-game or out-of-game browser + * @returns {boolean} + */ + var isInGame = function(){ + var inGame = false; + if(typeof CCPEVE === 'object'){ + inGame = true; + } +return true; + return inGame; + } + + + var requestTrust = function(){ + if(isInGame()){ + + var config = { + name: 'modules/dialog', + position: $('body'), + link: 'after', + functions: { + after: function(){ + $( "#pf_trust_dialog" ).dialog({ + modal: true, + buttons: { + Ok: function(){ + $(this).dialog('close'); + } + } + }); + } + } + }; + + var data = { + id: 'pf_trust_dialog', + titel: 'Trust page', + content: 123 //CCPEVE.requestTrust(Config.baseUrl) + }; + // Render.showModule(config, data); + } + }; + + + return { + requestTrust: requestTrust + }; +}); \ No newline at end of file diff --git a/js/app/client.js b/js/app/client.js new file mode 100644 index 00000000..a341333b --- /dev/null +++ b/js/app/client.js @@ -0,0 +1,32 @@ +/** + * Client + */ + +define(["jquery"], function($) { + + "use strict"; + + // client object + function Client(){} + + + var getClient = function(){ + var headerData = getHeaderData(); + }; + + // private functions ======================================================== + var getHeaderData = function(){ + console.log('TERST'); + $.ajax({ + url: 'http://localhost/exodus4d/pathfinder/', + headers:{'foo':'bar'}, + complete: function() { + alert(this.headers.foo); + } + }); + }; + + return { + getClient: getClient + }; +}); \ No newline at end of file diff --git a/js/app/contextmenu.js b/js/app/contextmenu.js new file mode 100644 index 00000000..62ab6e01 --- /dev/null +++ b/js/app/contextmenu.js @@ -0,0 +1,71 @@ +define(["jquery"], function($) { + + "use strict"; + + $.fn.contextMenu = function (settings) { + + return this.each(function () { + + // Open context menu + $(this).on("pf:openContextMenu", function (e, originalEvent, component) { + + + //open menu + $(settings.menuSelector) + .show() + .css({ + position: "absolute", + left: getLeftLocation(originalEvent), + top: getTopLocation(originalEvent) + }) + .off('click') + .on('click', {component: component}, function (e) { + $(this).hide(); + + var params = { + selectedMenu: $(e.target), + component: e.data.component + }; + + + settings.menuSelected.call(this, params); + }); + + + return false; + }); + + //make sure menu closes on any click + $(document).click(function () { + $(settings.menuSelector).hide(); + }); + }); + + function getLeftLocation(e) { + var mouseWidth = e.pageX; + var pageWidth = $(window).width(); + var menuWidth = $(settings.menuSelector).width(); + + // opening menu would pass the side of the page + if (mouseWidth + menuWidth > pageWidth && + menuWidth < mouseWidth) { + return mouseWidth - menuWidth; + } + return mouseWidth; + } + + function getTopLocation(e) { + var mouseHeight = e.pageY; + var pageHeight = $(window).height(); + var menuHeight = $(settings.menuSelector).height(); + + // opening menu would pass the bottom of the page + if (mouseHeight + menuHeight > pageHeight && + menuHeight < mouseHeight) { + return mouseHeight - menuHeight; + } + return mouseHeight; + } + + }; +}); \ No newline at end of file diff --git a/js/app/init.js b/js/app/init.js new file mode 100644 index 00000000..98d3e3e2 --- /dev/null +++ b/js/app/init.js @@ -0,0 +1,14 @@ +/** + * Init + */ + +define(["jquery"], function($) { + + "use strict"; + + var Config = { + baseUrl: "http://localhost/exodus4d/pathfinder/" // TODO: change baseURL + }; + + return Config; +}); \ No newline at end of file diff --git a/js/app/main.js b/js/app/main.js new file mode 100644 index 00000000..226db7de --- /dev/null +++ b/js/app/main.js @@ -0,0 +1,12 @@ +define(["jquery", "app/ccp", "app/map"], function($, CCP, Map) { + + "use strict"; + + $(function() { + //$('body').alpha().beta(); + + CCP.requestTrust(); + + Map.render(); + }); +}); \ No newline at end of file diff --git a/js/app/map.js b/js/app/map.js new file mode 100644 index 00000000..904b207b --- /dev/null +++ b/js/app/map.js @@ -0,0 +1,431 @@ +define(["jquery", "app/render", "jsPlumb", "app/contextmenu"], function($, Render) { + + "use strict"; + + var config = { + containerMapClass: 'pf-container-map', + mapId: 'pf-map', + systemClass: 'pf-system', + systemActiveClass: 'pf-system-active', + systemHeadClass: 'pf-system-head', + systemBody: 'pf-system-body', + contextMenuId: 'pf-map-contextmenu', + confirmDialogId: 'pf-delete-dialog', + + systemEffect: 'pf-system-effect', + systemEffectMagnetar: 'pf-system-effect-magnetar', + systemEffectRedGiant: 'pf-system-effect-redgiant', + systenEffectPular: 'pf-system-effect-pulsar', + systemEffectWolfRyet: 'pf-system-effect-wolfryet', + systemEffectCataclysmic: 'pf-system-effect-cataclysmic', + systemEffectBlackHole: 'pf-system-effect-blackhole', + + systemSec: 'pf-system-sec', + systemSecHigh: 'pf-system-sec-high', + systemSecLow: 'pf-system-sec-low', + systemSecNull: 'pf-system-sec-null', + systemSecWHHeigh: 'pf-system-sec-high', + systemSecWHMid: 'pf-system-sec-mid', + systemSecWHLow: 'pf-system-sec-low', + + systemStatusFriendly: 'pf-system-status-friendly', + systemStatusOccupied: 'pf-system-status-occupied', + systemStatusHostile: 'pf-system-status-hostile', + systemStatusEmpty: 'pf-system-status-empty', + systemStatusUnscanned: 'pf-system-status-unscanned' + }; + + var getEffectClassForSystem = function(effect){ + + var effectClass = ''; + + switch(effect){ + case 'magnetar': + effectClass = config.systemEffectMagnetar; + break; + case 'redGiant': + effectClass = config.systemEffectRedGiant; + break; + case 'pulsar': + effectClass = config.systenEffectPular; + break; + case 'wolfRyet': + effectClass = config.systemEffectWolfRyet; + break; + case 'cataclysmic': + effectClass = config.systemEffectCataclysmic; + break; + case 'blackHole': + effectClass = config.systemEffectBlackHole; + break; + } + + return effectClass; + }; + + var getSecurityClassForSystem = function(sec){ + + var secClass = ''; + + switch(sec){ + case 'H': + secClass = config.systemSecHigh; + break; + case 'L': + secClass = config.systemSecLow; + break; + case '0.0': + secClass = config.systemSecNull; + break; + case 'C6': + case 'C5': + secClass = config.systemSecWHHeigh; + break; + case 'C4': + case 'C3': + secClass = config.systemSecWHMid; + break; + case 'C2': + case 'C1': + secClass = config.systemSecWHLow; + break; + } + + return secClass; + }; + + var getStatusClassForSystem = function(status){ + + var statusClass = ''; + + switch(status){ + case 'friendly': + statusClass = config.systemStatusFriendly; + break; + case 'occupied': + statusClass = config.systemStatusOccupied; + break; + case 'hostile': + statusClass = config.systemStatusHostile; + break; + case 'empty': + statusClass = config.systemStatusEmpty; + break; + case 'unscanned': + statusClass = config.systemStatusUnscanned; + break; + } + + return statusClass; + }; + + + var getSystem = function(data){ + + var wh; + + // get system info classes + var effectClass = getEffectClassForSystem(data.effect); + var secClass = getSecurityClassForSystem(data.security); + var statusClass = getStatusClassForSystem(data.status); + + wh = $('
', { + // system + id: 'pf-system-' + data.id, + class: [config.systemClass, statusClass].join(' ') + }).append( + // system head + $('
', { + class: config.systemHeadClass, + text: data.name + }).append( + // System effect color + $('', { + class: ['fa fa-square ', config.systemEffect, effectClass].join(' ') + }) + ).prepend( + $('', { + class: [config.systemSec, secClass].join(' '), + text: data.security + }) + ) + + ).append( + // system body + $('
', { + class: config.systemBody + }) + ).css({ "left": data.position.x + "px", 'top': data.position.y + 'px' }); + + return wh; + }; + + var drawMap = function(){ + + var whData = [{ + id: 2, + name: 'J150020', + effect: 'magnetar', + security: 'C6', + status: 'friendly', + position: { + x: 0, + y: 0 + } + },{ + id: 3, + name: 'J115844', + effect: 'wolfRyet', + security: 'C6', + status: 'empty', + position: { + x: 60, + y: 60 + } + },{ + id: 4, + name: 'J155207', + effect: 'wolfRyet', + security: 'C6', + status: '', + position: { + x: 200, + y: 60 + } + },{ + id: 5, + name: 'J145510', + effect: 'pulsar', + security: 'C3', + status: 'hostile', + position: { + x: 110, + y: 110 + } + }]; + + // map + var map = $('#' + config.mapId); + + $.each(whData, function(i, data){ + var wh = getSystem(data); + + map.append(wh); + }); + }; + + /** + * load contextmenu template + */ + var initContextMenu = function(){ + + var moduleConfig = { + name: 'modules/contextmenu', + position: $('body'), + functions: { + after: function(){ + + } + } + }; + + var moduleData = { + id: config.contextMenuId, + items: [ + {icon: 'fa-eraser', action: 'delete', text: 'detach'}, + {icon: 'fa-info-circle', action: 'info', text: 'info'} + ] + }; + + Render.showModule(moduleConfig, moduleData); + + }; + + var render = function(){ + + // create map body + var mapContainer = $('
', { + id: config.mapId + }); + + $('.pf-container-map').append(mapContainer); + + // draw map + drawMap(); + + // add context menu to dom + initContextMenu(); + + // init jsPlumb + jsPlumb.ready(function() { + + var scope = 'wormhole'; + + var map = jsPlumb.getInstance({ + Container: config.mapId, + PaintStyle:{ + lineWidth: 5, // width of a Connector's line. An integer. + //fillStyle: "blue", // color for an Endpoint, eg. "blue", + strokeStyle: 'red', // color for a Connector + outlineColor: 'red', // color of the outline for an Endpoint or Connector. see fillStyle examples. + outlineWidth: 2 // width of the outline for an Endpoint or Connector. An integer. + //joinstyle:"round", + //dashstyle:"2 2", + }, + Connector:[ "Bezier", { curviness: 40, cssClass: 'pf-map-connection-default' } ], + Anchor: "Continuous", + Endpoint: 'Blank', + DragOptions : { cursor: "pointer", zIndex: 2000 } + }); + + var systemElements = jsPlumb.getSelector('.' + config.systemClass); + + var dropOptions = { + tolerance:"touch", + hoverClass:"dropHover", + activeClass:"dragActive" + }; + + var endpoint = { + endpoint: 'Blank', + // paintStyle:{ fillStyle: "blue", opacity: 0.5 }, + + //Connector:[ "Bezier", { curviness: 40, cssClass: 'pf-map-connection-default' } ], + isSource:true, + scope: scope, + isTarget:true, + dropOptions : dropOptions + }; + + map.doWhileSuspended(function() { + + map.registerConnectionType('normal', { + paintStyle:{ lineWidth:5, outlineWidth:2 }, + cssClass: 'pf-map-connection-default' + }); + + map.registerConnectionType('frig', { + paintStyle:{ lineWidth:5, outlineWidth:2 }, + cssClass: 'pf-map-connection-frig' + }); + + map.draggable(systemElements, { + containment:"parent" + }); + + var isFilterSupported = map.isDragFilterSupported(); + + if (isFilterSupported) { + map.makeSource(systemElements, { + filter: "." + config.systemHeadClass, + anchor: "Continuous", + connector: [ "Bezier", { curviness: 40, cssClass: 'pf-map-connection-default' } ], + scope: scope, + //parent: 'parent', + maxConnections: 5, + onMaxConnections:function(info, e) { + alert("Maximum connections (" + info.maxConnections + ") reached"); + } + }); + } + + map.makeTarget(systemElements, { + filter: "." + config.systemHeadClass, + anchor: "Continuous", + dropOptions:{ hoverClass: config.systemActiveClass }, + allowLoopback: false, + scope: scope + }); + + map.addEndpoint(systemElements, { + }, endpoint); + + + map.connect({ + source: $('#pf-system-3'), + target: $('#pf-system-4'), + //anchor: 'Continuous' + type: 'normal', + endpoint: 'Blank' + }); + + // set observer ======================================================== + + // Context Menu on connections + map.bind("contextmenu", function(component, e) { + // trigger menu "open" + $(e.target).trigger('pf:openContextMenu', [e, component]); + e.preventDefault(); + return false; + }); + + jsPlumb.fire("pf-map-loaded", map); + }); + + /** + * init context menu for all connections + * must be triggered manually on demand + */ + $('path').contextMenu({ + menuSelector: "#" + config.contextMenuId, + menuSelected: function (params) { + + var action = params.selectedMenu.attr('data-action'); + + switch(action){ + case 'delete': + // delete a single connection + + // confirm dialog + var moduleConfig = { + name: 'modules/dialog', + position: $('body'), + link: 'after', + functions: { + after: function(){ + $( "#" + config.confirmDialogId).dialog({ + modal: true, + buttons: { + 'Cancel': function(){ + $(this).dialog('close'); + }, + 'Yes': function(){ + map.detach(params.component); + $(this).dialog('close'); + } + } + }); + } + } + }; + + var modulData = { + id: config.confirmDialogId, + titel: 'Delete Connection', + content: 123 + }; + + Render.showModule(moduleConfig, modulData); + + + break; + case 'info': console.log('info') + break; + } + + + + + } + }); + + }); + + + + }; + + return { + render: render + }; +}); \ No newline at end of file diff --git a/js/app/render.js b/js/app/render.js new file mode 100644 index 00000000..fd0aadc3 --- /dev/null +++ b/js/app/render.js @@ -0,0 +1,63 @@ +/** + * Render controller + */ + +define(["jquery", "lib/mustache", "jqueryUI"], function($, Mustache) { + + "use strict"; + + /** + * init function will be called before and after a new module is loaded + * @param functionName + * @param config + */ + var initModule = function(functionName, config){ + + if( + typeof config.functions === 'object' && + typeof config.functions[functionName] === 'function' + ){ + config.functions[functionName](); + } + }; + + /** + * load a template and render is with Mustache and/or Bootstrap + * @param config + * @param data + */ + var showModule = function(config, data){ + + // require module template + requirejs(['text!templates/' + config.name + '.html', "bootstrap"], function(template) { + + // check for an id, if module already exists, do not insert again + if( + data.id === 'undefined' || + $("#" + data.id).length === 0 + ){ + + var content = Mustache.render(template, data); + + // display module + switch(config.link){ + case 'after': + config.position.after(content); + break; + default: + config.position.append(content); + } + } + + // init module function after render + initModule('after', config); + + + }); + }; + + + return { + showModule: showModule + }; +}); \ No newline at end of file diff --git a/js/lib/bootstrap-sprockets.js b/js/lib/bootstrap-sprockets.js new file mode 100644 index 00000000..1abde496 --- /dev/null +++ b/js/lib/bootstrap-sprockets.js @@ -0,0 +1,12 @@ +//= require ./bootstrap/affix +//= require ./bootstrap/alert +//= require ./bootstrap/button +//= require ./bootstrap/carousel +//= require ./bootstrap/collapse +//= require ./bootstrap/dropdown +//= require ./bootstrap/tab +//= require ./bootstrap/transition +//= require ./bootstrap/scrollspy +//= require ./bootstrap/modal +//= require ./bootstrap/tooltip +//= require ./bootstrap/popover diff --git a/js/lib/bootstrap.js b/js/lib/bootstrap.js new file mode 100644 index 00000000..30409f48 --- /dev/null +++ b/js/lib/bootstrap.js @@ -0,0 +1,2107 @@ +/* ======================================================================== + * Bootstrap: affix.js v3.2.0 + * http://getbootstrap.com/javascript/#affix + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // AFFIX CLASS DEFINITION + // ====================== + + var Affix = function (element, options) { + this.options = $.extend({}, Affix.DEFAULTS, options) + + this.$target = $(this.options.target) + .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) + .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) + + this.$element = $(element) + this.affixed = + this.unpin = + this.pinnedOffset = null + + this.checkPosition() + } + + Affix.VERSION = '3.2.0' + + Affix.RESET = 'affix affix-top affix-bottom' + + Affix.DEFAULTS = { + offset: 0, + target: window + } + + Affix.prototype.getPinnedOffset = function () { + if (this.pinnedOffset) return this.pinnedOffset + this.$element.removeClass(Affix.RESET).addClass('affix') + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + return (this.pinnedOffset = position.top - scrollTop) + } + + Affix.prototype.checkPositionWithEventLoop = function () { + setTimeout($.proxy(this.checkPosition, this), 1) + } + + Affix.prototype.checkPosition = function () { + if (!this.$element.is(':visible')) return + + var scrollHeight = $(document).height() + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + var offset = this.options.offset + var offsetTop = offset.top + var offsetBottom = offset.bottom + + if (typeof offset != 'object') offsetBottom = offsetTop = offset + if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) + if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) + + var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false : + offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' : + offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false + + if (this.affixed === affix) return + if (this.unpin != null) this.$element.css('top', '') + + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger($.Event(affixType.replace('affix', 'affixed'))) + + if (affix == 'bottom') { + this.$element.offset({ + top: scrollHeight - this.$element.height() - offsetBottom + }) + } + } + + + // AFFIX PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.affix') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.affix', (data = new Affix(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.affix + + $.fn.affix = Plugin + $.fn.affix.Constructor = Affix + + + // AFFIX NO CONFLICT + // ================= + + $.fn.affix.noConflict = function () { + $.fn.affix = old + return this + } + + + // AFFIX DATA-API + // ============== + + $(window).on('load', function () { + $('[data-spy="affix"]').each(function () { + var $spy = $(this) + var data = $spy.data() + + data.offset = data.offset || {} + + if (data.offsetBottom) data.offset.bottom = data.offsetBottom + if (data.offsetTop) data.offset.top = data.offsetTop + + Plugin.call($spy, data) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.2.0 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.VERSION = '3.2.0' + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.hasClass('alert') ? $this : $this.parent() + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + // detach from parent, fire event then clean up data + $parent.detach().trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one('bsTransitionEnd', removeElement) + .emulateTransitionEnd(150) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.alert + + $.fn.alert = Plugin + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.2.0 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.VERSION = '3.2.0' + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state = state + 'Text' + + if (data.resetText == null) $el.data('resetText', $el[val]()) + + $el[val](data[state] == null ? this.options[state] : data[state]) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked') && this.$element.hasClass('active')) changed = false + else $parent.find('.active').removeClass('active') + } + if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } + + if (changed) this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + var old = $.fn.button + + $.fn.button = Plugin + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document).on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + Plugin.call($btn, 'toggle') + e.preventDefault() + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.2.0 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this)) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = + this.sliding = + this.interval = + this.$active = + this.$items = null + + this.options.pause == 'hover' && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } + + Carousel.VERSION = '3.2.0' + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true + } + + Carousel.prototype.keydown = function (e) { + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return + } + + e.preventDefault() + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || $active[type]() + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var fallback = type == 'next' ? 'first' : 'last' + var that = this + + if (!$next.length) { + if (!this.options.wrap) return + $next = this.$element.find('.item')[fallback]() + } + + if ($next.hasClass('active')) return (this.sliding = false) + + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } + + var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one('bsTransitionEnd', function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { + that.$element.trigger(slidEvent) + }, 0) + }) + .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var href + var $this = $(this) + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + if (!$target.hasClass('carousel')) return + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + Plugin.call($target, options) + + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + }) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.2.0 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.transitioning = null + + if (this.options.parent) this.$parent = $(this.options.parent) + if (this.options.toggle) this.toggle() + } + + Collapse.VERSION = '3.2.0' + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var actives = this.$parent && this.$parent.find('> .panel > .in') + + if (actives && actives.length) { + var hasData = actives.data('bs.collapse') + if (hasData && hasData.transitioning) return + Plugin.call(actives, 'hide') + hasData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element[dimension](this.$element[dimension]())[0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse') + .removeClass('in') + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .trigger('hidden.bs.collapse') + .removeClass('collapsing') + .addClass('collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(350) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && option == 'show') option = !option + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.collapse + + $.fn.collapse = Plugin + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { + var href + var $this = $(this) + var target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + var $target = $(target) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + var parent = $this.attr('data-parent') + var $parent = parent && $(parent) + + if (!data || !data.transitioning) { + if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed') + $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + } + + Plugin.call($target, option) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.2.0 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.2.0' + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $('