Correct way to include a jQuery plugin
-
We want to use a jquery plugin (jqSimpleConnect.js) to draw div connector lines; As we don't have access to the angularjs environment in designer per se, is there a preferred way to (1) wrap a jQuery plugin in a directive/component and (2) declare the external js file?
I believe I read that you do load jQuery in your .app. Thanks - sorry if this has been answered elsewhere - couldn't find it.
Code FYI:
/* * «Copyright 2012 José F. Maldonado» /** * Declare namespace */ jqSimpleConnect = new Object(); /** * This member is an auxiliary counter used for generate unique identifiers. */ jqSimpleConnect._idGenerator = 0; /** * This member is an associative array which contains all the document's connections. */ jqSimpleConnect._connections = new Object(); /** * Positions a connection, acording to the position of the elements which connects. * * @param {object} connection A connection object. */ jqSimpleConnect._positionConnection = function(connection) { // Calculate the positions of the element's center. var posA = connection.elementA.offset(); posA.left = parseInt(posA.left, 10) + parseInt(connection.elementA.outerWidth()/2, 10); posA.top = parseInt(posA.top, 10) + parseInt(connection.elementA.outerHeight()/2, 10); var posB = connection.elementB.offset(); posB.left = parseInt(posB.left, 10) + parseInt(connection.elementB.outerWidth()/2, 10); posB.top = parseInt(posB.top, 10) + parseInt(connection.elementB.outerHeight()/2, 10); // Get the line's elements. var line1 = jQuery('#' + connection.id + '_1'); var line2 = jQuery('#' + connection.id + '_2'); var line3 = jQuery('#' + connection.id + '_3'); // Verify if the elements are aligned in a horizontal or vertical line. if(posA.left == posB.left || posA.top == posB.top) { // Uses only one line (hide the other two). line1.show(); line2.hide(); line3.hide(); // Verify if the line must be vertical or horizonal. if(posA.left == posB.left) { // Vertical line. jqSimpleConnect._positionVerticalLine(line1, posA, posB, connection.radius, connection.roundedCorners); } else { // Horizontal line. jqSimpleConnect._positionHorizontalLine(line1, posA, posB, connection.radius, connection.roundedCorners); } } else { // Verify if must use two lines or three. if(connection.anchorA != connection.anchorB) { // Use two lines (hide the third). line1.show(); line2.show(); line3.hide(); // Check the anchors of the elements. var corner = new Object(); if(connection.anchorA == 'vertical') { // Find the corner's position. corner.left = posA.left; corner.top = posB.top; // Draw lines. jqSimpleConnect._positionVerticalLine(line1, posA, corner, connection.radius, connection.roundedCorners); jqSimpleConnect._positionHorizontalLine(line2, posB, corner, connection.radius, connection.roundedCorners); } else { // Find the corner's position. corner.left = posB.left; corner.top = posA.top; // Draw lines. jqSimpleConnect._positionVerticalLine(line1, posB, corner, connection.radius, connection.roundedCorners); jqSimpleConnect._positionHorizontalLine(line2, posA, corner, connection.radius, connection.roundedCorners); } } else { // Use three lines. line1.show(); line2.show(); line3.show(); // Declare connection points. var corner1 = new Object(); var corner2 = new Object(); // Find if the middle's line must be vertical o horizontal. if(connection.anchorA == 'vertical') { // Middle's line must be horizontal. corner1.top = parseInt((posA.top + posB.top)/2, 10); corner2.top = corner1.top; corner1.left = posA.left; corner2.left = posB.left; // Draw lines. jqSimpleConnect._positionVerticalLine(line1, posA, corner1, connection.radius, connection.roundedCorners); jqSimpleConnect._positionVerticalLine(line2, posB, corner2, connection.radius, connection.roundedCorners); jqSimpleConnect._positionHorizontalLine(line3, corner1, corner2, connection.radius, connection.roundedCorners); } else { // Middle's line must be vertical. corner1.left = parseInt((posA.left + posB.left)/2, 10); corner2.left = corner1.left; corner1.top = posA.top; corner2.top = posB.top; // Draw lines. jqSimpleConnect._positionHorizontalLine(line1, posA, corner1, connection.radius, connection.roundedCorners); jqSimpleConnect._positionHorizontalLine(line2, posB, corner2, connection.radius, connection.roundedCorners); jqSimpleConnect._positionVerticalLine(line3, corner1, corner2, connection.radius, connection.roundedCorners); } } } } /** * Draws a vertical line, between the two points, by changing the properties of a HTML element. * *@param {object} jqElement A jQuery object of the HTML element used for represent the line. *@param {object} point1 An object with the properties 'left' and 'top' representing the position of the first point. *@param {object} point2 An object with the properties 'left' and 'top' representing the position of the second point. *@param {integer} radius The line's radius. *@param {boolean} roundedCorners A boolean indicating if the corners are going to be round. */ jqSimpleConnect._positionVerticalLine = function(jqElement, point1, point2, radius, roundedCorners) { var halfRadius = parseInt(radius/2, 10); jqElement.css('left', point1.left - halfRadius); jqElement.css('top', ((point1.top > point2.top)? (point2.top - halfRadius) : (point1.top - halfRadius))); jqElement.css('width', radius + 'px'); jqElement.css('height', ((point1.top > point2.top)? (point1.top - point2.top + radius) : (point2.top - point1.top + radius) ) + 'px'); } /** * Draws a horizontal line, between the two points, by changing the properties of a HTML element. * *@param {object} jqElement A jQuery object of the HTML element used for represent the line. *@param {object} point1 An object with the properties 'left' and 'top' representing the position of the first point. *@param {object} point2 An object with the properties 'left' and 'top' representing the position of the second point. *@param {integer} radius The line's radius. *@param {boolean} roundedCorners A boolean indicating if the corners are going to be round. */ jqSimpleConnect._positionHorizontalLine = function(jqElement, point1, point2, radius, roundedCorners) { var halfRadius = parseInt(radius/2, 10); jqElement.css('top', point1.top - halfRadius); jqElement.css('left', ((point1.left > point2.left)? (point2.left - halfRadius) : (point1.left - halfRadius))); jqElement.css('height', radius + 'px'); jqElement.css('width', ((point1.left > point2.left)? (point1.left - point2.left + radius) : (point2.left - point1.left + radius) ) + 'px'); } /** * Draws a connection between two elements. * * @param {object} elementA A CSS selector or a jQuery's object for select the first element. * @param {object} elementB A CSS selector or a jQuery's object for select the second element. * @param {object} options An associative array with the properties 'color' (which defines the color of the connection), 'radius' (the width of the * connection), 'roundedCorners' (a boolean indicating if the corners must be round), 'anchorA' (the anchor type of the first element, which can be * 'horizontal' or 'vertical') and 'anchorB' (the anchor type of second element). * @returns {string} The connection identifier or 'null' if the connection could not be draw. */ jqSimpleConnect.connect = function(elementA, elementB, options) { // Verify if the element's selector are ok. if(elementA == null || jQuery(elementA).length == 0 || elementB == null || jQuery(elementB).length == 0) { return null; } elementA = jQuery(elementA); if(elementA.length > 1) elementA = elementA.first(); elementB = jQuery(elementB); if(elementB.length > 1) elementB = elementB.first(); // Create connection object. var connection = new Object(); connection.id = 'jqSimpleConnect_' + jqSimpleConnect._idGenerator++; connection.elementA = elementA; connection.elementB = elementB; connection.color = (options != null && options.color != null)? options.color + '' : '#808080'; connection.radius = (options != null && options.radius != null && !isNaN(options.radius))? parseInt(options.radius, 10) : 5; connection.anchorA = (options != null && options.anchorA != null && (options.anchorA == 'vertical' || options.anchorA == 'horizontal'))? options.anchorA : 'horizontal'; connection.anchorB = (options != null && options.anchorB != null && (options.anchorB == 'vertical' || options.anchorB == 'horizontal'))? options.anchorB : 'horizontal'; connection.roundedCorners = options != null && options.roundedCorners != null && (options.roundedCorners == true || options.roundedCorners == 'true'); // Add connection to the connection's list. jqSimpleConnect._connections[connection.id] = connection; // Create HTML elements. var div = '<div id="divUniqueIdentifier" class="jqSimpleConnect '+connection.id+'" ' + 'style="width:'+connection.radius+'px; ' + 'height:'+connection.radius+'px; ' + 'background-color:'+connection.color+'; ' + (connection.roundedCorners? 'border-radius:'+parseInt(connection.radius/2,10)+'px; -webkit-border-radius:'+parseInt(connection.radius/2,10)+'px; -moz-border-radius:'+parseInt(connection.radius/2,10)+'px; ' : '') + 'position:absolute;"></div>'; jQuery('body').prepend(div.replace('divUniqueIdentifier', connection.id + '_1')); jQuery('body').prepend(div.replace('divUniqueIdentifier', connection.id + '_2')); jQuery('body').prepend(div.replace('divUniqueIdentifier', connection.id + '_3')); // Position connection. jqSimpleConnect._positionConnection(connection); // Return result. return connection.id; } /** * Repaints a connection. * * @param {string} connectionId The connection identifier. * @returns {boolean} 'true' if the operation was done, 'false' if the connection no exists. */ jqSimpleConnect.repaintConnection = function(connectionId) { var connection = jqSimpleConnect._connections[connectionId]; if(connection != null) { jqSimpleConnect._positionConnection(connection); return true; } return false; } /** * Repaints all the connections. */ jqSimpleConnect.repaintAll = function() { for(var key in jqSimpleConnect._connections) { jqSimpleConnect._positionConnection(jqSimpleConnect._connections[key]); } } /** * Removes a connection. * * @param {string} connectionId The connection identifier. * @returns {boolean} 'true' if the operation was done, 'false' if the connection no exists. */ jqSimpleConnect.removeConnection = function(connectionId) { if(jqSimpleConnect._connections[connectionId] != null) { // Remove HTML element. jQuery('.jqSimpleConnect.'+connectionId).remove(); // Remove connection data. jqSimpleConnect._connections[connectionId] = null; delete jqSimpleConnect._connections[connectionId]; // Return result. return true; } return false; } /** * Removes all the connections. */ jqSimpleConnect.removeAll = function() { // Remove HTML elements. jQuery('.jqSimpleConnect').remove(); // Clear connections list. for(var key in jqSimpleConnect._connections) { jqSimpleConnect._connections[key] = null; delete jqSimpleConnect._connections[key]; } }
-
I know this is old, but I'm working on something similar in SVG. I took a stab at modifying jqSimpleConnect, and it appears to work in a Mango user module. Here's what I did in case @BobDay still needs it or anyone else finds it useful.
First, I just put a copy of jqSimpleConnect.js in my public filestore, importing it in the define section of my user module. That let me start playing with it in the Chrome dev console until I could get it working. It defaults to putting the lines in the body, which are hidden behind the Mango UI. I tweaked it to use the Mango UI view area where custom pages are displayed and accounted for relative positioning. Finally, I wrapped it in a service.
I won't assert that this is correct way, but on a simple test page I could successfully recreate the basic jqSimpleConnect example.
In the user module, reference it in the three locations needed to make it a service.
define([ 'angular', 'require', ... './services/simpleConnectService.js', ], function(angular, require, ..., simpleConnectService) { 'use strict'; var yourModule = angular.module('yourModule', []); yourModule.service('simpleConnect', [simpleConnectService]); ...
I didn't bother actually testing directives. I just injected it into a random test component I had, logged it to the console, saved as variable, and played with it interactively from there.
temp1.connect('#div_a', '#div_b', {radius: 8, color: 'green'});
Now for the wrapped/modified jQuery. I left comments to show what I changed.
define([], function() { 'use strict'; // SimpleConnectService wraps jqSimpleConnect as an AngularJS service for Mango. // There are a few tweaks to make it work with the Mango dashboards, see "NOTE:" comments for modifications. function SimpleConnectService() { /* * «Copyright 2012 José F. Maldonado» * * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * Declare namespace */ // NOTE: Use the service itself. // jqSimpleConnect = new Object(); let jqSimpleConnect = this; /** * This member is an auxiliary counter used for generate unique identifiers. */ jqSimpleConnect._idGenerator = 0; /** * This member is an associative array which contains all the document's connections. */ jqSimpleConnect._connections = new Object(); /** * Positions a connection, acording to the position of the elements which connects. * * @param {object} connection A connection object. */ jqSimpleConnect._positionConnection = function(connection) { // Calculate the positions of the element's center. // NOTE: Get the position of the Mango UI view for displaying custom pages. let uiOffset = jQuery('div.ma-ui-main-view > div[ui-view]').offset(); let uiPos = jQuery('div.ma-ui-main-view > div[ui-view]').position(); uiPos.left = parseInt(uiOffset.left) - parseInt(uiPos.left); uiPos.top = parseInt(uiOffset.top) - parseInt(uiPos.top); // Calculate the positions of the element's center. var posA = connection.elementA.offset(); // NOTE: Use the UI offset to calculate a relative offset. // posA.left = parseInt(posA.left, 10) + parseInt(connection.elementA.outerWidth()/2, 10); // posA.top = parseInt(posA.top, 10) + parseInt(connection.elementA.outerHeight()/2, 10); posA.left = parseInt(posA.left, 10) - uiPos.left + parseInt(connection.elementA.outerWidth()/2, 10); posA.top = parseInt(posA.top, 10) - uiPos.top + parseInt(connection.elementA.outerHeight()/2, 10); var posB = connection.elementB.offset(); // NOTE: Use the UI offset to calculate a relative offset. // posB.left = parseInt(posB.left, 10) + parseInt(connection.elementB.outerWidth()/2, 10); // posB.top = parseInt(posB.top, 10) + parseInt(connection.elementB.outerHeight()/2, 10); posB.left = parseInt(posB.left, 10) - uiPos.left + parseInt(connection.elementB.outerWidth()/2, 10); posB.top = parseInt(posB.top, 10) - uiPos.top + parseInt(connection.elementB.outerHeight()/2, 10); // Get the line's elements. var line1 = jQuery('#' + connection.id + '_1'); var line2 = jQuery('#' + connection.id + '_2'); var line3 = jQuery('#' + connection.id + '_3'); // Verify if the elements are aligned in a horizontal or vertical line. if(posA.left == posB.left || posA.top == posB.top) { // Uses only one line (hide the other two). line1.show(); line2.hide(); line3.hide(); // Verify if the line must be vertical or horizonal. if(posA.left == posB.left) { // Vertical line. jqSimpleConnect._positionVerticalLine(line1, posA, posB, connection.radius, connection.roundedCorners); } else { // Horizontal line. jqSimpleConnect._positionHorizontalLine(line1, posA, posB, connection.radius, connection.roundedCorners); } } else { // Verify if must use two lines or three. if(connection.anchorA != connection.anchorB) { // Use two lines (hide the third). line1.show(); line2.show(); line3.hide(); // Check the anchors of the elements. var corner = new Object(); if(connection.anchorA == 'vertical') { // Find the corner's position. corner.left = posA.left; corner.top = posB.top; // Draw lines. jqSimpleConnect._positionVerticalLine(line1, posA, corner, connection.radius, connection.roundedCorners); jqSimpleConnect._positionHorizontalLine(line2, posB, corner, connection.radius, connection.roundedCorners); } else { // Find the corner's position. corner.left = posB.left; corner.top = posA.top; // Draw lines. jqSimpleConnect._positionVerticalLine(line1, posB, corner, connection.radius, connection.roundedCorners); jqSimpleConnect._positionHorizontalLine(line2, posA, corner, connection.radius, connection.roundedCorners); } } else { // Use three lines. line1.show(); line2.show(); line3.show(); // Declare connection points. var corner1 = new Object(); var corner2 = new Object(); // Find if the middle's line must be vertical o horizontal. if(connection.anchorA == 'vertical') { // Middle's line must be horizontal. corner1.top = parseInt((posA.top + posB.top)/2, 10); corner2.top = corner1.top; corner1.left = posA.left; corner2.left = posB.left; // Draw lines. jqSimpleConnect._positionVerticalLine(line1, posA, corner1, connection.radius, connection.roundedCorners); jqSimpleConnect._positionVerticalLine(line2, posB, corner2, connection.radius, connection.roundedCorners); jqSimpleConnect._positionHorizontalLine(line3, corner1, corner2, connection.radius, connection.roundedCorners); } else { // Middle's line must be vertical. corner1.left = parseInt((posA.left + posB.left)/2, 10); corner2.left = corner1.left; corner1.top = posA.top; corner2.top = posB.top; // Draw lines. jqSimpleConnect._positionHorizontalLine(line1, posA, corner1, connection.radius, connection.roundedCorners); jqSimpleConnect._positionHorizontalLine(line2, posB, corner2, connection.radius, connection.roundedCorners); jqSimpleConnect._positionVerticalLine(line3, corner1, corner2, connection.radius, connection.roundedCorners); } } } } /** * Draws a vertical line, between the two points, by changing the properties of a HTML element. * *@param {object} jqElement A jQuery object of the HTML element used for represent the line. *@param {object} point1 An object with the properties 'left' and 'top' representing the position of the first point. *@param {object} point2 An object with the properties 'left' and 'top' representing the position of the second point. *@param {integer} radius The line's radius. *@param {boolean} roundedCorners A boolean indicating if the corners are going to be round. */ jqSimpleConnect._positionVerticalLine = function(jqElement, point1, point2, radius, roundedCorners) { var halfRadius = parseInt(radius/2, 10); jqElement.css('left', point1.left - halfRadius); jqElement.css('top', ((point1.top > point2.top)? (point2.top - halfRadius) : (point1.top - halfRadius))); jqElement.css('width', radius + 'px'); jqElement.css('height', ((point1.top > point2.top)? (point1.top - point2.top + radius) : (point2.top - point1.top + radius) ) + 'px'); } /** * Draws a horizontal line, between the two points, by changing the properties of a HTML element. * *@param {object} jqElement A jQuery object of the HTML element used for represent the line. *@param {object} point1 An object with the properties 'left' and 'top' representing the position of the first point. *@param {object} point2 An object with the properties 'left' and 'top' representing the position of the second point. *@param {integer} radius The line's radius. *@param {boolean} roundedCorners A boolean indicating if the corners are going to be round. */ jqSimpleConnect._positionHorizontalLine = function(jqElement, point1, point2, radius, roundedCorners) { var halfRadius = parseInt(radius/2, 10); jqElement.css('top', point1.top - halfRadius); jqElement.css('left', ((point1.left > point2.left)? (point2.left - halfRadius) : (point1.left - halfRadius))); jqElement.css('height', radius + 'px'); jqElement.css('width', ((point1.left > point2.left)? (point1.left - point2.left + radius) : (point2.left - point1.left + radius) ) + 'px'); } /** * Draws a connection between two elements. * * @param {object} elementA A CSS selector or a jQuery's object for select the first element. * @param {object} elementB A CSS selector or a jQuery's object for select the second element. * @param {object} options An associative array with the properties 'color' (which defines the color of the connection), 'radius' (the width of the * connection), 'roundedCorners' (a boolean indicating if the corners must be round), 'anchorA' (the anchor type of the first element, which can be * 'horizontal' or 'vertical') and 'anchorB' (the anchor type of second element). * @returns {string} The connection identifier or 'null' if the connection could not be draw. */ jqSimpleConnect.connect = function(elementA, elementB, options) { // Verify if the element's selector are ok. // NOTE: Use length instead of size(). AngularJS/Mango's jQuery doesn't have size. // if(elementA == null || jQuery(elementA).size() == 0 || // elementB == null || jQuery(elementB).size() == 0) { // return null; // } if(elementA == null || jQuery(elementA).length == 0 || elementB == null || jQuery(elementB).length == 0) { // NOTE: Yell at me when I make a typo. Remove when it's working, or make this more useful. console.warn('failed to locate elements'); return null; } // NOTE: Use length instead of size() here too. elementA = jQuery(elementA); // if(elementA.size() > 1) elementA = elementA.first(); if(elementA.length > 1) elementA = elementA.first(); elementB = jQuery(elementB); // if(elementB.size() > 1) elementB = elementB.first(); if(elementB.length > 1) elementB = elementB.first(); // Create connection object. var connection = new Object(); connection.id = 'jqSimpleConnect_' + jqSimpleConnect._idGenerator++; connection.elementA = elementA; connection.elementB = elementB; connection.color = (options != null && options.color != null)? options.color + '' : '#808080'; connection.radius = (options != null && options.radius != null && !isNaN(options.radius))? parseInt(options.radius, 10) : 5; connection.anchorA = (options != null && options.anchorA != null && (options.anchorA == 'vertical' || options.anchorA == 'horizontal'))? options.anchorA : 'horizontal'; connection.anchorB = (options != null && options.anchorB != null && (options.anchorB == 'vertical' || options.anchorB == 'horizontal'))? options.anchorB : 'horizontal'; connection.roundedCorners = options != null && options.roundedCorners != null && (options.roundedCorners == true || options.roundedCorners == 'true'); connection.click = (options != null && options.click != null)? options.click : null; connection.mouseover = (options != null && options.mouseover != null)? options.mouseover : null; connection.mouseout = (options != null && options.mouseout != null)? options.mouseout : null; // Add connection to the connection's list. jqSimpleConnect._connections[connection.id] = connection; // Create HTML elements. var div = '<div id="divUniqueIdentifier" class="jqSimpleConnect '+connection.id+'" ' + 'style="width:'+connection.radius+'px; ' + 'height:'+connection.radius+'px; ' + 'background-color:'+connection.color+'; ' + (connection.roundedCorners? 'border-radius:'+parseInt(connection.radius/2,10)+'px; -webkit-border-radius:'+parseInt(connection.radius/2,10)+'px; -moz-border-radius:'+parseInt(connection.radius/2,10)+'px; ' : '') + 'position:absolute;"></div>'; // NOTE: 'body' will place elements behind the page. Instead of messing with z-index, use the Mango UI view. // jQuery('body').prepend(div.replace('divUniqueIdentifier', connection.id + '_1')); // jQuery('body').prepend(div.replace('divUniqueIdentifier', connection.id + '_2')); // jQuery('body').prepend(div.replace('divUniqueIdentifier', connection.id + '_3')); jQuery('div.ma-ui-main-view > div[ui-view]').prepend(div.replace('divUniqueIdentifier', connection.id + '_1')); jQuery('div.ma-ui-main-view > div[ui-view]').prepend(div.replace('divUniqueIdentifier', connection.id + '_2')); jQuery('div.ma-ui-main-view > div[ui-view]').prepend(div.replace('divUniqueIdentifier', connection.id + '_3')); jQuery("."+connection.id).click(function() { if(typeof connection.click == "function") { connection.click(connection); } }).on("mouseover", function() { if(typeof connection.mouseover == "function") { connection.mouseover(connection); } }).on("mouseout", function() { if(typeof connection.mouseout == "function") { connection.mouseout(connection); } }); // Position connection. jqSimpleConnect._positionConnection(connection); // Return result. return connection.id; } /** * Repaints a connection. * * @param {string} connectionId The connection identifier. * @returns {boolean} 'true' if the operation was done, 'false' if the connection no exists. */ jqSimpleConnect.repaintConnection = function(connectionId) { var connection = jqSimpleConnect._connections[connectionId]; if(connection != null) { jqSimpleConnect._positionConnection(connection); return true; } return false; } /** * Repaints all the connections. */ jqSimpleConnect.repaintAll = function() { for(var key in jqSimpleConnect._connections) { jqSimpleConnect._positionConnection(jqSimpleConnect._connections[key]); } } /** * Removes a connection. * * @param {string} connectionId The connection identifier. * @returns {boolean} 'true' if the operation was done, 'false' if the connection no exists. */ jqSimpleConnect.removeConnection = function(connectionId) { if(jqSimpleConnect._connections[connectionId] != null) { // Remove HTML element. jQuery('.jqSimpleConnect.'+connectionId).remove(); // Remove connection data. jqSimpleConnect._connections[connectionId] = null; delete jqSimpleConnect._connections[connectionId]; // Return result. return true; } return false; } /** * Removes all the connections. */ jqSimpleConnect.removeAll = function() { // Remove HTML elements. jQuery('.jqSimpleConnect').remove(); // Clear connections list. for(var key in jqSimpleConnect._connections) { jqSimpleConnect._connections[key] = null; delete jqSimpleConnect._connections[key]; } } // End of jqSimpleConnect. } return SimpleConnectService; });