I have been searching for quite a while now and I'm not able to find a simple JS library to make a network in which:
The nodes have labels
The edge thickness is proportional to the weight
Able to spread the nodes so that it doesn't get messy.
I found a script on bl.ocks.org which fits my needs, but when I use a lot of nodes it's getting quite messy:
I use this as input:
target,source,weight
woooooow,notgood,70.0
woooooow,gainzz,32.004950495049506
woooooow,test,33.86138613861386
woooooow,peanutss,20.866336633663366
nicecode,woooooow,22.103960396039604
woooooow,oreo,20.742574257425744
bread,woooooow,37.945544554455445
jam,nutella,20.123762376237625
bread,nutsarelol,20.866336633663366
etc
Question
Any ideas which code/library I can use to create a graph like the graph above but with more spreading of the nodes?
JS code
/*
Author: Corneliu S. (github.com/upphiminn)
This is a javascript implementation of the Louvain
community detection algorithm (http://arxiv.org/abs/0803.0476)
Based on https://bitbucket.org/taynaud/python-louvain/overview
*/
(function(){
jLouvain = function(){
//Constants
var __PASS_MAX = -1
var __MIN = 0.0000001
//Local vars
var original_graph_nodes;
var original_graph_edges;
var original_graph = {};
var partition_init;
//Helpers
function make_set(array){
var set = {};
array.forEach(function(d,i){
set[d] = true;
});
return Object.keys(set);
};
function obj_values(obj){
var vals = [];
for( var key in obj ) {
if ( obj.hasOwnProperty(key) ) {
vals.push(obj[key]);
}
}
return vals;
};
function get_degree_for_node(graph, node){
var neighbours = graph._assoc_mat[node] ? Object.keys(graph._assoc_mat[node]) : [];
var weight = 0;
neighbours.forEach(function(neighbour,i){
var value = graph._assoc_mat[node][neighbour] || 1;
if(node == neighbour)
value *= 2;
weight += value;
});
return weight;
};
function get_neighbours_of_node(graph, node){
if(typeof graph._assoc_mat[node] == 'undefined')
return [];
var neighbours = Object.keys(graph._assoc_mat[node]);
return neighbours;
}
function get_edge_weight(graph, node1, node2){
return graph._assoc_mat[node1] ? graph._assoc_mat[node1][node2] : undefined;
}
function get_graph_size(graph){
var size = 0;
graph.edges.forEach(function(edge){
size += edge.weight;
});
return size;
}
function add_edge_to_graph(graph, edge){
update_assoc_mat(graph, edge);
var edge_index = graph.edges.map(function(d){
return d.source+'_'+d.target;
}).indexOf(edge.source+'_'+edge.target);
if(edge_index != -1)
graph.edges[edge_index].weight = edge.weight;
else
graph.edges.push(edge);
}
function make_assoc_mat(edge_list){
var mat = {};
edge_list.forEach(function(edge, i){
mat[edge.source] = mat[edge.source] || {};
mat[edge.source][edge.target] = edge.weight;
mat[edge.target] = mat[edge.target] || {};
mat[edge.target][edge.source] = edge.weight;
});
return mat;
}
function update_assoc_mat(graph, edge){
graph._assoc_mat[edge.source] = graph._assoc_mat[edge.source] || {};
graph._assoc_mat[edge.source][edge.target] = edge.weight;
graph._assoc_mat[edge.target] = graph._assoc_mat[edge.target] || {};
graph._assoc_mat[edge.target][edge.source] = edge.weight;
}
function clone(obj){
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = obj.constructor();
for(var key in obj)
temp[key] = clone(obj[key]);
return temp;
}
//Core-Algorithm Related
function init_status(graph, status, part){
status['nodes_to_com'] = {};
status['total_weight'] = 0;
status['internals'] = {};
status['degrees'] = {};
status['gdegrees'] = {};
status['loops'] = {};
status['total_weight'] = get_graph_size(graph);
if(typeof part == 'undefined'){
graph.nodes.forEach(function(node,i){
status.nodes_to_com[node] = i;
var deg = get_degree_for_node(graph, node);
if (deg < 0)
throw 'Bad graph type, use positive weights!';
status.degrees[i] = deg;
status.gdegrees[node] = deg;
status.loops[node] = get_edge_weight(graph, node, node) || 0;
status.internals[i] = status.loops[node];
});
}else{
graph.nodes.forEach(function(node,i){
var com = part[node];
status.nodes_to_com[node] = com;
var deg = get_degree_for_node(graph, node);
status.degrees[com] = (status.degrees[com] || 0) + deg;
status.gdegrees[node] = deg;
var inc = 0.0;
var neighbours = get_neighbours_of_node(graph, node);
neighbours.forEach(function(neighbour, i){
var weight = graph._assoc_mat[node][neighbour];
if (weight <= 0){
throw "Bad graph type, use positive weights";
}
if(part[neighbour] == com){
if (neighbour == node){
inc += weight;
}else{
inc += weight/2.0;
}
}
});
status.internals[com] = (status.internals[com] || 0) + inc;
});
}
}
function __modularity(status){
var links = status.total_weight;
var result = 0.0;
var communities = make_set(obj_values(status.nodes_to_com));
communities.forEach(function(com,i){
var in_degree = status.internals[com] || 0 ;
var degree = status.degrees[com] || 0 ;
if(links > 0){
result = result + in_degree / links - Math.pow((degree / (2.0*links)), 2);
}
});
return result;
}
function __neighcom(node, graph, status){
// compute the communities in the neighb. of the node, with the graph given by
// node_to_com
var weights = {};
var neighboorhood = get_neighbours_of_node(graph, node);//make iterable;
neighboorhood.forEach(function(neighbour, i){
if(neighbour != node){
var weight = graph._assoc_mat[node][neighbour] || 1;
var neighbourcom = status.nodes_to_com[neighbour];
weights[neighbourcom] = (weights[neighbourcom] || 0) + weight;
}
});
return weights;
}
function __insert(node, com, weight, status){
//insert node into com and modify status
status.nodes_to_com[node] = +com;
status.degrees[com] = (status.degrees[com] || 0) + (status.gdegrees[node]||0);
status.internals[com] = (status.internals[com] || 0) + weight + (status.loops[node]||0);
}
function __remove(node, com, weight, status){
//remove node from com and modify status
status.degrees[com] = ((status.degrees[com] || 0) - (status.gdegrees[node] || 0));
status.internals[com] = ((status.internals[com] || 0) - weight -(status.loops[node] ||0));
status.nodes_to_com[node] = -1;
}
function __renumber(dict){
var count = 0;
var ret = clone(dict); //deep copy :)
var new_values = {};
var dict_keys = Object.keys(dict);
dict_keys.forEach(function(key){
var value = dict[key];
var new_value = typeof new_values[value] =='undefined' ? -1 : new_values[value];
if(new_value == -1){
new_values[value] = count;
new_value = count;
count = count + 1;
}
ret[key] = new_value;
});
return ret;
}
function __one_level(graph, status){
//Compute one level of the Communities Dendogram.
var modif = true,
nb_pass_done = 0,
cur_mod = __modularity(status),
new_mod = cur_mod;
while (modif && nb_pass_done != __PASS_MAX){
cur_mod = new_mod;
modif = false;
nb_pass_done += 1
graph.nodes.forEach(function(node,i){
var com_node = status.nodes_to_com[node];
var degc_totw = (status.gdegrees[node] || 0) / (status.total_weight * 2.0);
var neigh_communities = __neighcom(node, graph, status);
__remove(node, com_node, (neigh_communities[com_node] || 0.0), status);
var best_com = com_node;
var best_increase = 0;
var neigh_communities_entries = Object.keys(neigh_communities);//make iterable;
neigh_communities_entries.forEach(function(com,i){
var incr = neigh_communities[com] - (status.degrees[com] || 0.0) * degc_totw;
if (incr > best_increase){
best_increase = incr;
best_com = com;
}
});
__insert(node, best_com, neigh_communities[best_com] || 0, status);
if(best_com != com_node)
modif = true;
});
new_mod = __modularity(status);
if(new_mod - cur_mod < __MIN)
break;
}
}
function induced_graph(partition, graph){
var ret = {nodes:[], edges:[], _assoc_mat: {}};
var w_prec, weight;
//add nodes from partition values
var partition_values = obj_values(partition);
ret.nodes = ret.nodes.concat(make_set(partition_values)); //make set
graph.edges.forEach(function(edge,i){
weight = edge.weight || 1;
var com1 = partition[edge.source];
var com2 = partition[edge.target];
w_prec = (get_edge_weight(ret, com1, com2) || 0);
var new_weight = (w_prec + weight);
add_edge_to_graph(ret, {'source': com1, 'target': com2, 'weight': new_weight});
});
return ret;
}
function partition_at_level(dendogram, level){
var partition = clone(dendogram[0]);
for(var i = 1; i < level + 1; i++ )
Object.keys(partition).forEach(function(key,j){
var node = key;
var com = partition[key];
partition[node] = dendogram[i][com];
});
return partition;
}
function generate_dendogram(graph, part_init){
if(graph.edges.length == 0){
var part = {};
graph.nodes.forEach(function(node,i){
part[node] = node;
});
return part;
}
var status = {};
init_status(original_graph, status, part_init);
var mod = __modularity(status);
var status_list = [];
__one_level(original_graph, status);
var new_mod = __modularity(status);
var partition = __renumber(status.nodes_to_com);
status_list.push(partition);
mod = new_mod;
var current_graph = induced_graph(partition, original_graph);
init_status(current_graph, status);
while (true){
__one_level(current_graph, status);
new_mod = __modularity(status);
if(new_mod - mod < __MIN)
break;
partition = __renumber(status.nodes_to_com);
status_list.push(partition);
mod = new_mod;
current_graph = induced_graph(partition, current_graph);
init_status(current_graph, status);
}
return status_list;
}
var core = function(){
var status = {};
var dendogram = generate_dendogram(original_graph, partition_init);
return partition_at_level(dendogram, dendogram.length - 1);
};
core.nodes = function(nds){
if(arguments.length > 0){
original_graph_nodes = nds;
}
return core;
};
core.edges = function(edgs){
if(typeof original_graph_nodes == 'undefined')
throw 'Please provide the graph nodes first!';
if(arguments.length > 0){
original_graph_edges = edgs;
var assoc_mat = make_assoc_mat(edgs);
original_graph = { 'nodes': original_graph_nodes,
'edges': original_graph_edges,
'_assoc_mat': assoc_mat };
}
return core;
};
core.partition_init = function(prttn){
if(arguments.length > 0){
partition_init = prttn;
}
return core;
};
return core;
}
})();
Related
On a MouseEvent instance, we have a property called path, it might look like this:
Does anybody know if there is a reliable way to translate this path array into an XPath? I assume that this path data is the best data to start from? Is there a library I can use to do the conversion?
This library looks promising, but it doesn't use the path property of an event: https://github.com/johannhof/xpath-dom
There's not a unique XPath to a node, so you'll have to decide what's the most appropriate way of constructing a path. Use IDs where available? Numeral position in the document? Position relative to other elements?
See getPathTo() in this answer for one possible approach.
PS: Taken from Javascript get XPath of a node
Another option is to use SelectorGadget from below link
https://dv0akt2986vzh.cloudfront.net/unstable/lib/selectorgadget.js
The actual code for the DOM path is at
https://dv0akt2986vzh.cloudfront.net/stable/lib/dom.js
Usage: on http://google.com
elem = document.getElementById("q")
predict = new DomPredictionHelper()
predict.pathOf(elem)
// gives "body.default-theme.des-mat:nth-child(2) div#_Alw:nth-child(4) form#f:nth-child(2) div#fkbx:nth-child(2) input#q:nth-child(2)"
predict.predictCss([elem],[])
// gives "#q"
CODE if link goes down
// Copyright (c) 2008, 2009 Andrew Cantino
// Copyright (c) 2008, 2009 Kyle Maxwell
function DomPredictionHelper() {};
DomPredictionHelper.prototype = new Object();
DomPredictionHelper.prototype.recursiveNodes = function(e){
var n;
if(e.nodeName && e.parentNode && e != document.body) {
n = this.recursiveNodes(e.parentNode);
} else {
n = new Array();
}
n.push(e);
return n;
};
DomPredictionHelper.prototype.escapeCssNames = function(name) {
if (name) {
try {
return name.replace(/\s*sg_\w+\s*/g, '').replace(/\\/g, '\\\\').
replace(/\./g, '\\.').replace(/#/g, '\\#').replace(/\>/g, '\\>').replace(/\,/g, '\\,').replace(/\:/g, '\\:');
} catch(e) {
console.log('---');
console.log("exception in escapeCssNames");
console.log(name);
console.log('---');
return '';
}
} else {
return '';
}
};
DomPredictionHelper.prototype.childElemNumber = function(elem) {
var count = 0;
while (elem.previousSibling && (elem = elem.previousSibling)) {
if (elem.nodeType == 1) count++;
}
return count;
};
DomPredictionHelper.prototype.pathOf = function(elem){
var nodes = this.recursiveNodes(elem);
var self = this;
var path = "";
for(var i = 0; i < nodes.length; i++) {
var e = nodes[i];
if (e) {
path += e.nodeName.toLowerCase();
var escaped = e.id && self.escapeCssNames(new String(e.id));
if(escaped && escaped.length > 0) path += '#' + escaped;
if(e.className) {
jQuery.each(e.className.split(/ /), function() {
var escaped = self.escapeCssNames(this);
if (this && escaped.length > 0) {
path += '.' + escaped;
}
});
}
path += ':nth-child(' + (self.childElemNumber(e) + 1) + ')';
path += ' '
}
}
if (path.charAt(path.length - 1) == ' ') path = path.substring(0, path.length - 1);
return path;
};
DomPredictionHelper.prototype.commonCss = function(array) {
try {
var dmp = new diff_match_patch();
} catch(e) {
throw "Please include the diff_match_patch library.";
}
if (typeof array == 'undefined' || array.length == 0) return '';
var existing_tokens = {};
var encoded_css_array = this.encodeCssForDiff(array, existing_tokens);
var collective_common = encoded_css_array.pop();
jQuery.each(encoded_css_array, function(e) {
var diff = dmp.diff_main(collective_common, this);
collective_common = '';
jQuery.each(diff, function() {
if (this[0] == 0) collective_common += this[1];
});
});
return this.decodeCss(collective_common, existing_tokens);
};
DomPredictionHelper.prototype.tokenizeCss = function(css_string) {
var skip = false;
var word = '';
var tokens = [];
var css_string = css_string.replace(/,/, ' , ').replace(/\s+/g, ' ');
var length = css_string.length;
var c = '';
for (var i = 0; i < length; i++){
c = css_string[i];
if (skip) {
skip = false;
} else if (c == '\\') {
skip = true;
} else if (c == '.' || c == ' ' || c == '#' || c == '>' || c == ':' || c == ',') {
if (word.length > 0) tokens.push(word);
word = '';
}
word += c;
if (c == ' ' || c == ',') {
tokens.push(word);
word = '';
}
}
if (word.length > 0) tokens.push(word);
return tokens;
};
DomPredictionHelper.prototype.decodeCss = function(string, existing_tokens) {
var inverted = this.invertObject(existing_tokens);
var out = '';
jQuery.each(string.split(''), function() {
out += inverted[this];
});
return this.cleanCss(out);
};
// Encode css paths for diff using unicode codepoints to allow for a large number of tokens.
DomPredictionHelper.prototype.encodeCssForDiff = function(strings, existing_tokens) {
var codepoint = 50;
var self = this;
var strings_out = [];
jQuery.each(strings, function() {
var out = new String();
jQuery.each(self.tokenizeCss(this), function() {
if (!existing_tokens[this]) {
existing_tokens[this] = String.fromCharCode(codepoint++);
}
out += existing_tokens[this];
});
strings_out.push(out);
});
return strings_out;
};
DomPredictionHelper.prototype.simplifyCss = function(css, selected_paths, rejected_paths) {
var self = this;
var parts = self.tokenizeCss(css);
var best_so_far = "";
if (self.selectorGets('all', selected_paths, css) && self.selectorGets('none', rejected_paths, css)) best_so_far = css;
for (var pass = 0; pass < 4; pass++) {
for (var part = 0; part < parts.length; part++) {
var first = parts[part].substring(0,1);
if (self.wouldLeaveFreeFloatingNthChild(parts, part)) continue;
if ((pass == 0 && first == ':') || // :nth-child
(pass == 1 && first != ':' && first != '.' && first != '#' && first != ' ') || // elem, etc.
(pass == 2 && first == '.') || // classes
(pass == 3 && first == '#')) // ids
{
var tmp = parts[part];
parts[part] = '';
var selector = self.cleanCss(parts.join(''));
if (selector == '') {
parts[part] = tmp;
continue;
}
if (self.selectorGets('all', selected_paths, selector) && self.selectorGets('none', rejected_paths, selector)) {
best_so_far = selector;
} else {
parts[part] = tmp;
}
}
}
}
return self.cleanCss(best_so_far);
};
DomPredictionHelper.prototype.wouldLeaveFreeFloatingNthChild = function(parts, part) {
return (((part - 1 >= 0 && parts[part - 1].substring(0, 1) == ':') &&
(part - 2 < 0 || parts[part - 2] == ' ') &&
(part + 1 >= parts.length || parts[part + 1] == ' ')) ||
((part + 1 < parts.length && parts[part + 1].substring(0, 1) == ':') &&
(part + 2 >= parts.length || parts[part + 2] == ' ') &&
(part - 1 < 0 || parts[part - 1] == ' ')));
};
DomPredictionHelper.prototype.cleanCss = function(css) {
return css.replace(/\>/, ' > ').replace(/,/, ' , ').replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '').replace(/,$/, '');
};
DomPredictionHelper.prototype.getPathsFor = function(arr) {
var self = this;
var out = [];
jQuery.each(arr, function() {
if (this && this.nodeName) {
out.push(self.pathOf(this));
}
})
return out;
};
DomPredictionHelper.prototype.predictCss = function(s, r) {
var self = this;
if (s.length == 0) return '';
var selected_paths = self.getPathsFor(s);
var rejected_paths = self.getPathsFor(r);
var css = self.commonCss(selected_paths);
var simplest = self.simplifyCss(css, selected_paths, rejected_paths);
// Do we get off easy?
if (simplest.length > 0) return simplest;
// Okay, then make a union and possibly try to reduce subsets.
var union = '';
jQuery.each(s, function() {
union = self.pathOf(this) + ", " + union;
});
union = self.cleanCss(union);
return self.simplifyCss(union, selected_paths, rejected_paths);
};
DomPredictionHelper.prototype.fragmentSelector = function(selector) {
var self = this;
var out = [];
jQuery.each(selector.split(/\,/), function() {
var out2 = [];
jQuery.each(self.cleanCss(this).split(/\s+/), function() {
out2.push(self.tokenizeCss(this));
});
out.push(out2);
});
return out;
};
// Everything in the first selector must be present in the second.
DomPredictionHelper.prototype.selectorBlockMatchesSelectorBlock = function(selector_block1, selector_block2) {
for (var j = 0; j < selector_block1.length; j++) {
if (jQuery.inArray(selector_block1[j], selector_block2) == -1) {
return false;
}
}
return true;
};
// Assumes list is an array of complete CSS selectors represented as strings.
DomPredictionHelper.prototype.selectorGets = function(type, list, the_selector) {
var self = this;
var result = true;
if (list.length == 0 && type == 'all') return false;
if (list.length == 0 && type == 'none') return true;
var selectors = self.fragmentSelector(the_selector);
var cleaned_list = [];
jQuery.each(list, function() {
cleaned_list.push(self.fragmentSelector(this)[0]);
});
jQuery.each(selectors, function() {
if (!result) return;
var selector = this;
jQuery.each(cleaned_list, function(pos) {
if (!result || this == '') return;
if (self._selectorGets(this, selector)) {
if (type == 'none') result = false;
cleaned_list[pos] = '';
}
});
});
if (type == 'all' && cleaned_list.join('').length > 0) { // Some candidates didn't get matched.
result = false;
}
return result;
};
DomPredictionHelper.prototype._selectorGets = function(candidate_as_blocks, selector_as_blocks) {
var cannot_match = false;
var position = candidate_as_blocks.length - 1;
for (var i = selector_as_blocks.length - 1; i > -1; i--) {
if (cannot_match) break;
if (i == selector_as_blocks.length - 1) { // First element on right.
// If we don't match the first element, we cannot match.
if (!this.selectorBlockMatchesSelectorBlock(selector_as_blocks[i], candidate_as_blocks[position])) cannot_match = true;
position--;
} else {
var found = false;
while (position > -1 && !found) {
found = this.selectorBlockMatchesSelectorBlock(selector_as_blocks[i], candidate_as_blocks[position]);
position--;
}
if (!found) cannot_match = true;
}
}
return !cannot_match;
};
DomPredictionHelper.prototype.invertObject = function(object) {
var new_object = {};
jQuery.each(object, function(key, value) {
new_object[value] = key;
});
return new_object;
};
DomPredictionHelper.prototype.cssToXPath = function(css_string) {
var tokens = this.tokenizeCss(css_string);
if (tokens[0] && tokens[0] == ' ') tokens.splice(0, 1);
if (tokens[tokens.length - 1] && tokens[tokens.length - 1] == ' ') tokens.splice(tokens.length - 1, 1);
var css_block = [];
var out = "";
for(var i = 0; i < tokens.length; i++) {
if (tokens[i] == ' ') {
out += this.cssToXPathBlockHelper(css_block);
css_block = [];
} else {
css_block.push(tokens[i]);
}
}
return out + this.cssToXPathBlockHelper(css_block);
};
// Process a block (html entity, class(es), id, :nth-child()) of css
DomPredictionHelper.prototype.cssToXPathBlockHelper = function(css_block) {
if (css_block.length == 0) return '//';
var out = '//';
var first = css_block[0].substring(0,1);
if (first == ',') return " | ";
if (jQuery.inArray(first, [':', '#', '.']) != -1) {
out += '*';
}
var expressions = [];
var re = null;
for(var i = 0; i < css_block.length; i++) {
var current = css_block[i];
first = current.substring(0,1);
var rest = current.substring(1);
if (first == ':') {
// We only support :nth-child(n) at the moment.
if (re = rest.match(/^nth-child\((\d+)\)$/))
expressions.push('(((count(preceding-sibling::*) + 1) = ' + re[1] + ') and parent::*)');
} else if (first == '.') {
expressions.push('contains(concat( " ", #class, " " ), concat( " ", "' + rest + '", " " ))');
} else if (first == '#') {
expressions.push('(#id = "' + rest + '")');
} else if (first == ',') {
} else {
out += current;
}
}
if (expressions.length > 0) out += '[';
for (var i = 0; i < expressions.length; i++) {
out += expressions[i];
if (i < expressions.length - 1) out += ' and ';
}
if (expressions.length > 0) out += ']';
return out;
};
This is confusing me, i am trying to create an identifier like Jquery.
$.ajax
$('object')
the jquery identifier $ can be called without its parenthesis.
Here is some code i got:
function initialized_object(){
this.method = function(){
console.log('this is a string');
}
}
var o = function (args){
if(arguments.length > 0){
//return N$(arguments[0], arguments[1]);
}else{
return new initialized_object();
}
};
o.prototype.constructor.toString = function(){
this.call(this);
}
o().method();
Instead of o().method() i would like to use o.method()
I looked at the jquery source in attempt to find the solution to this with no avail: http://code.jquery.com/jquery-1.11.3.js
This is what i am working with:
( in case you have any ideas )
function N$_no_parameters(){
this.ajax = function(func){
func();
};
};
var N$_np = new N$_no_parameters();
var N$_CURRENT_EVENT_THIS = null;
function N$(selector, within){
this.co = "hi";
if (!Array.prototype.indexOf){
Array.prototype.indexOf = function(elt /*, from*/){
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0) ? Math.ceil(from) : Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++){
if (from in this && this[from] === elt)
return from;
}
return -1;
};
}
var DOM_N$ = function(selector, within, frame){
this.frame = frame || {
win: (within != undefined)? within.contentWindow || this.constructor.caller.arguments[0] : this.constructor.caller.arguments[0],
doc: (within != undefined)? within.contentDocument || this.constructor.caller.arguments[0].document : this.constructor.caller.arguments[0].document,
this: this.constructor.caller.caller.caller.caller.caller
};
if(selector instanceof Object){
if(selector.defaultView == this.frame.win){//selector is document
this.selector = selector;
this.nodes = [this.selector];
}else if(selector.document == document){//selector is window
this.selector = selector;
this.nodes = [this.selector];
}else if(selector instanceof this.frame.this){// selector is this
this.selector = N$_CURRENT_EVENT_THIS.selector;
this.nodes = N$_CURRENT_EVENT_THIS.nodes;//
}else if(selector instanceof Element){//selector is DOM
this.selector = selector;
this.nodes = [this.selector];
}else{
this.selector = selector;
this.nodes = new Array();
for(var key in selector){
var dom_n$_ = N$(selector[key]).nodes;
for(var key_ in dom_n$_){
if(this.nodes.indexOf(dom_n$_[key_]) == -1){
this.nodes.push(dom_n$_[key_]);
}
}
}
}
}else if(typeof selector == "string"){
this.selector = selector;
this.nodes = $prepare(this.selector, this.frame.doc);
}
this.event = function(event_, func){
var that = this;
actionair(function(node){
var events = node.events || {};
if(node.addEventListener){
if((event_) in events){
node.removeEventListener(event_, events[event_], true);
var tmp___ = events[event_];
var tmp__ = function(){
this.bar = "hello";
N$_CURRENT_EVENT_THIS = that;
tmp___(node, event_);
new func(node, event_);
N$_CURRENT_EVENT_THIS = null;
};
node.addEventListener(event_, tmp__, true);
events[event_] = tmp__;
}else{
var tmp__ = function(){
N$_CURRENT_EVENT_THIS = that;
new func(node, event_);
N$_CURRENT_EVENT_THIS = null;
};
node.addEventListener(event_, tmp__, true);
events[event_] = tmp__;
}
}else if(node.attachEvent){
var ie_event = 'on' + event_;
if(event_ in events){
node.attachEvent(ie_event, function(){
N$_CURRENT_EVENT_THIS = that;
new func(node, event_);
events[event_](node, event_);
N$_CURRENT_EVENT_THIS = null;
});
}else{
node.attachEvent(ie_event, function(){
N$_CURRENT_EVENT_THIS = that;
new func(node, event_);
N$_CURRENT_EVENT_THIS = null;
});
}
events[event_] = func;
}
node.events = events;
}, this);
}
this.removeEvent = function(event_){
actionair(function(node, that){
var events = node.events || {};
if(node.removeEventListener){
if((event_) in events){
node.removeEventListener(event_, events[event_], true);
events[event_] = null;
}
}else if(node.detachEvent){
var ie_event = 'on' + event_;
if((event_) in events){
node.detachEvent(ie_event, events[event_]);
delete events[event_];
}
}
}, this);
}
this.eachNode = function(func){
actionair(function(node, that){
N$_CURRENT_EVENT_THIS = N$(node);
new func(node);
}, this);
}
this.css = function(attr, value){
N$_CURRENT_EVENT_THIS = this;
var attribute = "";
if(attr.indexOf('-') !== -1){
var split_attr = attr.split('-');
for (var i = 0; i < split_attr.length; i++) {
if(i != 0)
attribute += split_attr[i].charAt(0).toUpperCase() + split_attr[i].slice(1);
else
attribute += split_attr[i].charAt(0).toLowerCase() + split_attr[i].slice(1);
};
}else{
attribute = attr;
}
var properties = new Array();
actionair(function(node, that){
if(typeof value != 'undefined'){
node.style[attribute] = value;
}
if (!that.frame.win.getComputedStyle) {//IE
that.frame.win.getComputedStyle = function(el, pseudo) {
that.el = el;
that.getPropertyValue = function(prop) {
var re = /(\-([a-z]){1})/g;
if (prop == 'float') prop = 'styleFloat';
if (re.test(prop)) {
prop = prop.replace(re, function () {
return arguments[2].toUpperCase();
});
}
return el.currentStyle[prop] ? el.currentStyle[prop] : null;
}
return that;
}
}
properties.push(that.frame.win.getComputedStyle(node, null).getPropertyValue(attr));
}, this);
return properties;
};
this.text = function(str){
actionair(function(node, that){
node.innerHTML = '';
node.appendChild(that.frame.doc.createTextNode(str));
}, this);
};
this.appendNode = function(tagname, innerHTML){
actionair(function(node, that){
var new_node = that.frame.doc.createElement(tagname);
new_node.innerHTML = innerHTML;
node.appendChild(new_node);
}, this);
};
this.innerHTML = function(innerHTML){
actionair(function(node, that){
node.innerHTML = innerHTML;
}, this);
};
this.removeNode = function(){
actionair(function(node, that){
node.parentNode.removeChild(node);
}, this);
};
this.animate = function(func, from, to, speed){
var that = this;
actionair(function(node, that){
(function animate(func, from, to, speed, node){
if(from >= to){
N$_CURRENT_EVENT_THIS = that;
new func(node, to);
N$_CURRENT_EVENT_THIS = null;
}else{
N$_CURRENT_EVENT_THIS = that;
new func(node, from);
N$_CURRENT_EVENT_THIS = null;
setTimeout(
function(){
animate(func, from+1, to, speed, node);
}, speed
);
}
})(func, from, to, speed, node);
}, this);
}
function actionair(func, that){
for (var i = 0; i < that.nodes.length; i++) {
(function(i_){
N$_CURRENT_EVENT_THIS = that;
new func(that.nodes[i_], that);
N$_CURRENT_EVENT_THIS = null;
})(i);
}
}
function $prepare(str, doc){
str = str.replace(/(\s+>\s+)/g,'>');
str = str.replace(/(\s+)/g,' ');
var str_ = str;
var querys = str.split(/[\s\>]+/);
var querys_des = Array();
var ascender = new Array();
for (var i = 0; i < str_.length; i++) {
if(str_[i] == ">" || str_[i] == " "){
var tmp_ = (str_[i] == ">")? 'next_child' : 'ascended';
ascender.push( tmp_);
}
};
var recognizes = new Array();
for (var i = 0; i < querys.length; i++) {
var asc_child = null;
asc_child = ascender[i-1];
var tmp_ = {
"selector": querys[i],
"i":i
};
recognizes[i] = recognize(querys[i], doc);
if(i != 0){
tmp_["asc_child"] = asc_child;
}else{
tmp_["base_selector"] = true;
}
querys_des.push(tmp_);
};
return $select(querys_des, recognizes, doc);
}
function $select(querys_des, recognizes, parent_, doc){
var parents = parent_ || null;
for (var i = 0; i < querys_des.length; i++) {
if('base_selector' in querys_des[i]){
parents = recognizes[querys_des[i]['i']];
}else if('asc_child' in querys_des[i]){
var cur_children = recognizes[querys_des[i]['i']];
if(querys_des[i]['asc_child'] == 'next_child'){
var compatible = compatible_children(parents, cur_children, querys_des[i]['asc_child'], doc);
parents = compatible;
}else if(querys_des[i]['asc_child'] == 'ascended'){
var compatible = compatible_children(parents, cur_children, querys_des[i]['asc_child'], doc);
parents = compatible;
}
}
};
return parents;
}
function compatible_children(parents, children, type, doc){
var ret = new Array();
for (var a = 0; a < parents.length; a++) {
for (var b = 0; b < children.length; b++) {
if(type == 'next_child'){
if(parents[a] == children[b].parentNode){
if(ret.indexOf(children[b]) == -1)
ret.push(children[b]);
}
}else if(type == 'ascended'){
if(isin(parents[a], children[b], doc)){
if(ret.indexOf(children[b]) == -1)
ret.push(children[b]);
}
}
}
}
return ret;
}
function isin(parent, child, doc){
var child_ = child;
var ret = new Array();
while((child_ = child_.parentNode) && child_ != doc.body){
if(parent == child_){
return true;
}
}
return false;
}
function recognize(str, doc){
var identifier = new Array();
var id_ = false;
var class_ = false;
var dom_ = false;
if(str.indexOf("#") >= 0){
id_ = true;
var tmp = str.split("#")[1];
if(str.indexOf(".") >= 0){
identifier['ID'] = tmp.split(".")[0];
}else{
identifier['ID'] = tmp;
}
}
if(str.indexOf(".") >= 0){
class_ = true;
var tmp = str.split(".")[1];
if(str.indexOf("#") >= 0){
identifier['CLASS'] = tmp.split("#")[0];
}else{
identifier['CLASS'] = tmp;
}
}
if(id_ && class_){
if(str.indexOf("#") < str.indexOf(".")){
var tmp = str.split("#")[0];
if(tmp.length > 0){
dom_ = true;
identifier['DOM'] = tmp;
}
}else{
var tmp = str.split(".")[0];
if(tmp.length > 0){
dom_ = true;
identifier['DOM'] = tmp;
}
}
}else if(id_){
var tmp = str.split("#")[0];
if(tmp.length > 0){
dom_ = true;
identifier['DOM'] = tmp;
}
}else if(class_){
var tmp = str.split(".")[0];
if(tmp.length > 0){
dom_ = true;
identifier['DOM'] = tmp;
}
}else{
if(str.length > 0){
dom_ = true;
identifier['DOM'] = str;
}
}
var x;
if(class_){
if(typeof doc.getElementsByClassName !== 'function') {//Old browsers
x = doc.body.getElementsByTagName("*");
}else{
x = doc.getElementsByClassName(identifier['CLASS']);
}
}else if(dom_){
x = doc.getElementsByTagName(identifier['DOM']);
}else if(id_){
x = doc.body.getElementsByTagName("*");
for (var i = 0; i < x.length; i++) {
if(x[i].getAttribute("id") != identifier['ID']){
delete x[i];
}
};
}
var elements = new Array();
for (var i = 0; i < x.length; i++) {
if(id_ && class_){
if(x[i].getAttribute("id") == identifier["ID"] && x[i].getAttribute("class") == identifier["CLASS"]){
if(dom_){
if(x[i].tagName.toLowerCase() == identifier['DOM'].toLowerCase()){
elements.push(x[i]);
}
}else{
elements.push(x[i]);
}
}
}else if(id_){
if(x[i].getAttribute("id") == identifier["ID"]){
if(dom_){
if(x[i].tagName.toLowerCase() == identifier['DOM'].toLowerCase()){
elements.push(x[i]);
}
}else{
elements.push(x[i]);
}
}
}else if(class_){
if(x[i].getAttribute("class") == identifier["CLASS"]){
if(dom_){
if(x[i].tagName.toLowerCase() == identifier['DOM'].toLowerCase()){
elements.push(x[i]);
}
}else{
elements.push(x[i]);
}
}
}else{
if(dom_){
if(x[i].tagName.toLowerCase() == identifier['DOM'].toLowerCase()){
elements.push(x[i]);
}
}else{
elements.push(x[i]);
}
}
};
return elements;
}
};
var selectors = new Array();
console.log('arguments' + arguments.length);
if(arguments.length > 0){
return new (function(selector, within){
if(typeof within == typeof {}){
if(within.nodes != undefined){
var ret = new Array();
for (var i = within.nodes.length - 1; i >= 0; i--) {
ret.push(do_node_select(selector, within.nodes[i]));
};
return ret;
}else if(
typeof Node === "object" ? within instanceof Node :
within && typeof within === "object" && typeof within.nodeType === "number" && typeof within.nodeName==="string"
){
return do_node_select(selector, within);
}
}
return do_node_select(selector, undefined);
function do_node_select(selector, node){
var N$_new = new ( function(win, doc){
return new DOM_N$(selector, node || undefined);
})(window);
var N$_ = null;
if(selectors.length > 0){
for (var i = selectors.length - 1; i >= 0; i--) {
if(selectors[i].selector == selector){
var not_in = new Array();
for (var b = N$_new.nodes.length - 1; b >= 0; b--) {
if(selectors[i].nodes.indexOf(N$_new.nodes[b]) == -1){
not_in.push(N$_new.nodes[b]);
}
};
for (var a = not_in.length - 1; a >= 0; a--) {
if(selectors[i].nodes.indexOf(not_in[a]) == -1){
selectors[i].nodes.push(not_in[a]);
}
};
N$_ = selectors[i];
break;
}else{
N$_ = N$_new;
}
};
}else{
N$_ = N$_new;
if(N$_.nodes.length > 0){
selectors.push(N$_);
}
}
return N$_;
}
})(selector, within || undefined);
}else{
return N$_np;
}
};
N$(window).event('load', function(){
N$.ajax(function(){ // this will not work but using N$().ajax will
console.log('aaa');
});
});
This is a library i am making similar to Jquery, it selects nodes & handles events and more. the reason i would like to call my ajax function without parenthesis is for clarity.
the jquery identifier $ can be called without its parenthesis.
No. The function isn't being called.
Functions are objects. Objects can have properties. This is simply accessing a property of the function object.
function foo() {
return 1;
}
foo.bar = 2;
alert(foo.bar);
alert(foo());
Instead of o().method() i would like to use o.method()
Those statements mean different things.
The first calls the method method of the value that is returned when you call the o function.
The second calls the method method of the o function itself.
The second doesn't touch the initialised object (or even create one) at all.
$ is a function. But functions in JavaScript are also objects, which in-turn can have functions.
Perhaps this pattern is closer to what you are looking for:
var o = {
method: function() {
}
};
o.method();
Every time you call o() you get a new instance that can behave differently based on how you call it.
If you were to just call o.method, how would it know which reference to use? To be like jQuery, you would do
o = o() // or o('something')
which would create a global singleton o that you could then call o.method.
Quentim's answer is completely correct. You're just acessing a property, no big deal. I just wanted to add that there's a way to "call a function without parenthesis" if you're using getters:
window.__defineGetter__("$", function() {
//This gets executed when you access $
console.log('oh hai');
return { foo: 'bar' }
});
Not that this should be done at all, but this is a way of achieving what's in the question title
I have problem with removing a timer while it not being used anymore in javascript ?
let say we have a variable tm for the timer and set it with window.setInterval, while it still running we replace it by a new setInterval, how possible to get rid of the first setInterval ?
example code
var tm = setInterval(function(){
console.log('tm1')
}, 10);
tm = setInterval(function(){
console.log('tm2')
}, 10)
this is the real problem
motion:function(ele,cssStart,cssEnd,duration,tangent, easing,loop, complete){
if(ele==null||typeof cssEnd !== 'string') throw new TypeError();
if(!$hd.isElement(ele) || !$hd(ele).isExists()) throw new TypeError();
var validRules = /(left|top|width|color|height|background-color|margin-left|margin-right|margin-top|margin-botom|border-color|padding-left|padding-right|padding-top|border-bottom-width|border-top-width|border-left-width|border-right-width|border-bottom-color|border-top-color|border-left-color|border-right-color|padding-bottom|border-width|opacity|font-size)\s?(?=:).[^;]*(?=;?)/gim;
// filtering css input
cssEnd = cssEnd.replace(/\s{2,}|\n|\t/gim,'').replace(/\s?(:)\s?/gi,'$1').match(validRules);
cssStart = !cssStart?[]:cssStart.replace(/\s{2,}|\n|\t/gim,'').replace(/\s(:)\s/gi,'$1').match(validRules);
if(!cssEnd) throw new Error('invalid css rules, please refer to the documentation about the valid css rules for animation');
// creating properties
var _cssEnd = [],
_paused = false,
_cssStart = [],
_tm = null,
_step,
_complete = typeof complete ==='function'?complete:null,
_loop = 0,
_easing = typeof easing ==='string'?easing.match(/^easein|easeout|easeinout$/)?easing:'easein':'easein',
_tangent = typeof tangent ==='string'?tangent in hd.classes.motionTangents ? tangent:'linear':'linear';
this.ele = $hd(ele),
this.duration = isNaN(duration)?1:parseFloat(duration),
this.loop = isNaN(loop)?0:parseInt(loop),
this.isPlaying = false;
this.complete = false;
// verifying the css rules of the end point
var verify = function(cssStart, cssEnd){
for (var i = 0; i < cssEnd.length; i++) {
var colorPattern = /#[a-z0-9]+|rgba?\s?\(([0-9]{1,3}\s?,\s?)+((([0-9]+)?(\.)?([0-9]+))+)?\)/gi,
name = cssEnd[i].replace(/^([a-z0-9-]+)\s?(?=:).*/gi,'$1'),
value = cssEnd[i].replace(/^[a-z0-9-]+\s?:(.*)/gi,'$1'),
startIndex = $hd(cssStart).inspectData(name+':',false),
startValue = !startIndex.length?this.ele.getcss(name):cssStart[startIndex].replace(/^[a-z0-9-]+:(.*)/gi,'$1');
if(value=='') continue;
// parsing values
if(name.match(/color/i)){
//if color
// validate the color
if(!value.match(colorPattern)) continue;
if(value.match(/#[a-z0-9]+/ig)){
// if hex then convert to rgb
var rgb = $hd(value).hex2rgb(),
value = !rgb?null:rgb;
if(!value) continue;
}
// verifying cssStart's value
startValue = !startValue.match(colorPattern)?this.ele.getcss(name):startValue;
if(!startValue.match(colorPattern)) continue;
if(startValue.match(/#[a-z0-9]+/ig)){
// if hex then convert to rgb
var rgb = $hd(startValue).hex2rgb(),
startValue = rgb==null?null:rgb;
}
// if browser doesn't support rgba then convert the value to rgb
value = !$hd.supports.rgba && value.match(/rgba/i)?value.replace(/(.*)a\s?(\((\d{1,3},)(\d{1,3},)(\d{1,3})).*/i,'$1$2)'):value;
startValue = !$hd.supports.rgba && startValue.match(/rgba/i)?startValue.replace(/(.*)a\s?(\((\d{1,3},)(\d{1,3},)(\d{1,3})).*/i,'$1$2)'):startValue;
// compare and convert the value of both to object
var colora = value.match(/[0-9]{1,3}/g),
colorb = startValue.match(/[0-9]{1,3}/g);
if(colora.length > colorb.length){
colorb.push(this.ele.getcss('opacity'))
}else if(colorb.length>colora.length){
colora.push(colorb[colorb.length-1])
}
_cssEnd.push({
type:'color',
name:name,
value:{
r:parseInt(colora[0]),
g:parseInt(colora[1]),
b:parseInt(colora[2]),
a:colora[3]?parseFloat(colora[3]):null
}
});
_cssStart.push({
type:'color',
name:name,
value:{
r:parseInt(colorb[0]),
g:parseInt(colorb[1]),
b:parseInt(colorb[2]),
a:colorb[3]?parseFloat(colorb[3]):null
}
});
}else{
value = parseFloat(value),
startValue = parseFloat((isNaN(parseFloat(startValue))?parseFloat(this.ele.getcss(name)):startValue));
if(isNaN(value)) continue;
if(isNaN(startValue)) startValue = 0;
_cssEnd.push({
type:'unit',
name:name,
value:parseFloat(value)
});
_cssStart.push({
type:'unit',
name:name,
value:parseFloat(startValue)
});
}
}
};
verify.apply(this,[cssStart,cssEnd]);
// clearing the arguments
cssStart = complete = cssEnd = duration = tangent = loop = ele = verify = null;
if($hd(_cssEnd).isEmpty()) throw new Error('MotionClass::invalid css rules');// raise error if cssEnd is empty
var _pauseTime = 0;
this.play = function(){
if(this.isPlaying) return;
if(!this.ele.isExists() || !this.ele.visible()) {
this.stop();
return;
}
this.isPlaying = true;
_paused = false;
if( $hd(_cssEnd).inspectData('left',true,true).length||
$hd(_cssEnd).inspectData('top',true, true).length)
this.ele.css('position:absolute');
var st = new Date() - _pauseTime;
_tm = window.setInterval(function(){
if(!this.ele.isExists() || !this.ele.visible()) {
this.stop();
return;
}
var pg, delta,
timePassed = new Date() - st;
pg = timePassed / (this.duration*1000);
if (pg > 1) pg = 1;
if(_easing === 'easeout'){
delta = 1 - hd.classes.motionTangents[_tangent](1 - pg);
}else if(_easing==='easeinout'){
if (pg <= 0.5) {
// the first half of animation will easing in
delta= hd.classes.motionTangents[_tangent](2 * pg) / 2
} else {
// the rest of animation will easing out
delta= (2 - hd.classes.motionTangents[_tangent](2 * (1 - pg))) / 2
}
}else{
delta = hd.classes.motionTangents[_tangent](pg);
}
// the movement
_step.call(this,delta);
if(_paused){
window.clearInterval(_tm);
_tm=null;
_pauseTime = timePassed;
this.isPlaying = false;
return;
}
if (pg == 1) {
_pauseTime =0;
if(_loop>=this.loop){
this.complete = true;
this.stop();
if(_complete)_complete.call(null,this);
}else{
_loop++;
st = new Date(),
timePassed = new Date() - st,
pg = 0;
}
}
}.bind(this),15);
};
_step = function(delta){
var styles = '';
for(var i=0;i<_cssEnd.length;i++){
var name = _cssEnd[i].name,
svalue =_cssStart[i].value,
value = _cssEnd[i].value;
if(_cssEnd[i].type == 'color'){
styles += name+':'+(value.a !=null?'rgba(':'rgb(')
+ Math.max(Math.min(parseInt((delta * (value.r-svalue.r)) + svalue.r, 10), 255), 0) + ','
+ Math.max(Math.min(parseInt((delta * (value.g-svalue.g)) + svalue.g, 10), 255), 0) + ','
+ Math.max(Math.min(parseInt((delta * (value.b-svalue.b)) + svalue.b, 10), 255), 0)
+ (value.a ==null?'':',' + $hd(parseFloat((value.a-svalue.a)*delta+svalue.a)).round(1)) +') !important;';
}else{
styles += name+':'+ $hd(parseFloat((value-svalue)*delta+svalue)).round(2) + (name.match(/opacity/i)?'':'px')+ '!important;';
}
this.ele.css(styles);
}
};
this.stop = function(){
this.isPlaying = false;
window.clearInterval(_tm);
_tm=null;
_loop = 0;
};
this.pause = function(){
_paused = true;
};
}
this how the problems came up;
var color = 'rgba('+($(1).randomize(0,255)+',')+
($(1).randomize(0,255)+',')+
($(1).randomize(0,255)+',')+
'1)';
var bcolor = 'rgb('+($(1).randomize(0,255)+',')+
($(1).randomize(0,255)+',')+
($(1).randomize(0,255)+',')+
')';
var motion = new hd.classes.motion(box,'',('height:'+($(1).randomize(10,50))+
'px;border-color:'+bcolor+
';background-color:'+color+
';left :'+((e.x || e.clientX)-(box.rectangle().width/2))+
';top : '+((e.y || e.clientY)-(box.rectangle().height/2))+
';border-top-width:'+($(1).randomize(0,50))),
2,'circle','easeinout',1, function(e){
status.html('Idle');
});
motion.play();
while variable motion has been referenced with an motion class (animation), how possible to stop the first motion's timer if it being replaced with new motion class(animation)
Really easy! clearInterval(tm). If you call them different names, you will be able to differentiate between them.
i am new to javascript and im making a BMI calculator. im making a class in javascript to produce the calculations. currently users could input their height (in feet and inches) and weight. when i run a method within my class. it keeps saying this.feet = null instead of grabbing the value from the input. my javascript code is below. result() is in my html for the submit button.
function calculator(feet,inches,weight) {
this.feet = feet.value;
this.inches = inches.value;
this.weight = weight.value;
this.validateInput = function() {
var errors = [];
if(isNaN(this.feet) || this.feet < 0) {
errors.push("Feet");
}
else {
return this.feet
};
if(isNaN(this.inches) || this.inches < 0 || this.inches > 11) {
errors.push("Inches")
}
else {
return this.inches;
};
if(isNaN(this.weight) || this.weight < 0) {
errors.push("Weight");
}
else {
return this.weight
}
};
this.inchesConverter = function() {
this.feet = parseFloat(feet);
this.inches = parseFloat(inches);
return parseInt(this.feet*12+this.inches);
};
this.bmi = function() {
var height = this.inchesConverter();
var validater = this.validateInput();
for(r = 0; r < errors.length; r++){
if(errors.length > 0) {
return errors[r] + " must be a valid positive number.";
}
else {
parseFloat((validateWeight * 703) / (Math.pow(height, 2))).toFixed(1);
}
}
};
};
var getWeight = document.getElementById('txtWeight');
var getFeet = document.getElementById('txtHeightFeet');
var getInches = document.getElementById('txtHeightInches');
var test = new calculator(getFeet, getInches, getWeight);
function result() {
document.getElementById("lblBMI").innerHTML(test.bmi());
}
Instead of assigning feet.value to this.feet try assigning feet.getAttribute('value')
If you console.log(feet) does it show the proper DOM element?
There is no need to declare the variables with "this."
function calculator(paraFeet,paraInches,paraWeight) {
var feet = paraFeet.value;
var inches = paraInches.value;
var weight = paraWeight.value;
...
you can now remove "this." from every of these variables inside your function
Every browser has JavaScript console on which you can debug your code.
I have some fun to fix it here or there but also you should try it yourself.
Anyway here it is:
function calculator(weight, feet, inches) {
this.weight = weight;
this.feet = feet;
this.inches = inches;
this.validateInput = function() {
var errors = [];
if (!this.validNumber(this.weight, 1000)) {
errors.push("Invalid weight.");
}
if (!this.validNumber(this.feet, 10)) {
errors.push("Invalid hight in feet.");
}
if (!this.validNumber(this.inches, 11)) {
errors.push("Invalid hight in inches.")
}
return errors;
};
this.validNumber = function(num, max) {
if (num.length > 0 && !isNaN(num)) {
var number = parseInt(num);
return number > 0 && number < max + 1;
}
return false;
}
this.displayErrors = function(errors) {
var html = "";
for (error of errors)
html += error + "<br/>";
return html;
};
this.inchesConverter = function() {
var feet = parseInt(this.feet);
var inches = parseInt(this.inches);
return feet * 12 + inches;
};
this.bmi = function() {
var errors = this.validateInput();
if (errors.length > 0) {
return this.displayErrors(errors);
}
var height = this.inchesConverter();
return parseFloat((this.weight * 703) / (Math.pow(height, 2))).toFixed(1);
};
};
function result() {
var getWeight = document.getElementById('txtWeight').value;
var getFeet = document.getElementById('txtHeightFeet').value;
var getInches = document.getElementById('txtHeightInches').value;
var test = new calculator(getWeight, getFeet, getInches);
document.getElementById("lblBMI").innerHTML = test.bmi();
return false;
}
<span>
<label>Weight</label>
<input id="txtWeight">
</span>
<span>
<label>HeightFeet</label>
<input id="txtHeightFeet">
</span>
<span>
<label>HeightInches</label>
<input id="txtHeightInches">
</span>
<button id='run' onClick="return result();">Calculate</button>
<div id="lblBMI"></div>
Figured out what my issue was. The problem arose from my for loop. Working code is posted below!
function calculator(feet,inches,weight) {
this.feet = feet.value;
this.inches = inches.value;
this.weight = weight.value;
this.validateInput = function() {
var errors = [];
if(isNaN(this.feet) || this.feet < 0 || this.feet == "") {
errors.push("feet");
};
if(isNaN(this.inches) || this.inches < 0 || this.inches > 11) {
errors.push("inches")
};
if(isNaN(this.weight) || this.weight < 0 || this.weight == "") {
errors.push("weight");
};
return errors;
console.log(errors);
};
this.inchesConverter = function() {
var parseFeet = parseFloat(this.feet);
var parseInches = parseFloat(this.inches);
return parseInt(parseFeet*12+parseInches);
};
this.bmi = function() {
var height = this.inchesConverter();
var errors = this.validateInput();
if(errors.length > 0) {
for(r = 0; r < errors.length; r++){
return "Please enter a correct number for " + errors[r];
};
}
else {
return parseFloat((this.weight * 703) / (Math.pow(height, 2))).toFixed(1);
};
};
};
function result(){
var getWeight = document.getElementById('txtWeight');
var getFeet = document.getElementById('txtHeightFeet');
var getInches = document.getElementById('txtHeightInches');
var test = new calculator(getFeet, getInches, getWeight);
document.getElementById("lblBMI").innerHTML = test.bmi();
};
I've found a script that seems perfect for my needs, but it uses IDs, rather than classes to create ipad-friendly drag & drop elements.
I really need it to use classes, as the draggable elements could potentially be in the thousands.
[edit] I'm not that great at javascript and am having difficulties in understanding how I could alter the script to use classes instead of IDs.
I have also contacted the script author, but have had no reply from him.
I'm offering this bounty as I've not had any response to my original query.
Please could someone change the script below so that it uses classes? [/edit]
Below is the script in its entirety, and here is the script page (API was not helpful to me in being able to use classes vs id).
// webkitdragdrop.js v1.0, Mon May 15 2010
//
// Copyright (c) 2010 Tommaso Buvoli (http://www.tommasobuvoli.com)
// No Extra Libraries are required, simply download this file, add it to your pages!
//
// To See this library in action, grab an ipad and head over to http://www.gotproject.com
// webkitdragdrop is freely distributable under the terms of an MIT-style license.
//Description
// Because this library was designed to run without requiring any other libraries, several basic helper functions were implemented
// 6 helper functons in this webkit_tools class have been taked directly from Prototype 1.6.1 (http://prototypejs.org/) (c) 2005-2009 Sam Stephenson
var webkit_tools =
{
//$ function - simply a more robust getElementById
$:function(e)
{
if(typeof(e) == 'string')
{
return document.getElementById(e);
}
return e;
},
//extend function - copies the values of b into a (Shallow copy)
extend:function(a,b)
{
for (var key in b)
{
a[key] = b[key];
}
return a;
},
//empty function - used as defaut for events
empty:function()
{
},
//remove null values from an array
compact:function(a)
{
var b = []
var l = a.length;
for(var i = 0; i < l; i ++)
{
if(a[i] !== null)
{
b.push(a[i]);
}
}
return b;
},
//DESCRIPTION
// This function was taken from the internet (http://robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/) and returns
// the computed style of an element independantly from the browser
//INPUT
// oELM (DOM ELEMENT) element whose style should be extracted
// strCssRule element
getCalculatedStyle:function(oElm, strCssRule)
{
var strValue = "";
if(document.defaultView && document.defaultView.getComputedStyle){
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
}
else if(oElm.currentStyle){
strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
},
//bindAsEventListener function - used to bind events
bindAsEventListener:function(f,object)
{
var __method = f;
return function(event) {
__method.call(object, event || window.event);
};
},
//cumulative offset - courtesy of Prototype (http://www.prototypejs.org)
cumulativeOffset:function(element)
{
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
if (element.offsetParent == document.body)
if (element.style.position == 'absolute') break;
element = element.offsetParent;
} while (element);
return {left : valueL, top : valueT};
},
//getDimensions - courtesy of Prototype (http://www.prototypejs.org)
getDimensions: function(element)
{
var display = element.style.display;
if (display != 'none' && display != null) // Safari bug
return {width: element.offsetWidth, height: element.offsetHeight};
var els = element.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
var originalDisplay = els.display;
els.visibility = 'hidden';
if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
els.position = 'absolute';
els.display = 'block';
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
return {width: originalWidth, height: originalHeight};
},
//hasClassName - courtesy of Prototype (http://www.prototypejs.org)
hasClassName: function(element, className)
{
var elementClassName = element.className;
return (elementClassName.length > 0 && (elementClassName == className ||
new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
},
//addClassName - courtesy of Prototype (http://www.prototypejs.org)
addClassName: function(element, className)
{
if (!this.hasClassName(element, className))
element.className += (element.className ? ' ' : '') + className;
return element;
},
//removeClassName - courtesy of Prototype (http://www.prototypejs.org)
removeClassName: function(element, className)
{
element.className = this.strip(element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' '));
return element;
},
//strip - courtesy of Prototype (http://www.prototypejs.org)
strip:function(s)
{
return s.replace(/^\s+/, '').replace(/\s+$/, '');
}
}
//Description
// Droppable fire events when a draggable is dropped on them
var webkit_droppables = function()
{
this.initialize = function()
{
this.droppables = [];
this.droppableRegions = [];
}
this.add = function(root, instance_props)
{
root = webkit_tools.$(root);
var default_props = {accept : [], hoverClass : null, onDrop : webkit_tools.empty, onOver : webkit_tools.empty, onOut : webkit_tools.empty};
default_props = webkit_tools.extend(default_props, instance_props || {});
this.droppables.push({r : root, p : default_props});
}
this.remove = function(root)
{
root = webkit_tools.$(root);
var d = this.droppables;
var i = d.length;
while(i--)
{
if(d[i].r == root)
{
d[i] = null;
this.droppables = webkit_tools.compact(d);
return true;
}
}
return false;
}
//calculate position and size of all droppables
this.prepare = function()
{
var d = this.droppables;
var i = d.length;
var dR = [];
var r = null;
while(i--)
{
r = d[i].r;
if(r.style.display != 'none')
{
dR.push({i : i, size : webkit_tools.getDimensions(r), offset : webkit_tools.cumulativeOffset(r)})
}
}
this.droppableRegions = dR;
}
this.finalize = function(x,y,r,e)
{
var indices = this.isOver(x,y);
var index = this.maxZIndex(indices);
var over = this.process(index,r);
if(over)
{
this.drop(index, r,e);
}
this.process(-1,r);
return over;
}
this.check = function(x,y,r)
{
var indices = this.isOver(x,y);
var index = this.maxZIndex(indices);
return this.process(index,r);
}
this.isOver = function(x, y)
{
var dR = this.droppableRegions;
var i = dR.length;
var active = [];
var r = 0;
var maxX = 0;
var minX = 0;
var maxY = 0;
var minY = 0;
while(i--)
{
r = dR[i];
minY = r.offset.top;
maxY = minY + r.size.height;
if((y > minY) && (y < maxY))
{
minX = r.offset.left;
maxX = minX + r.size.width;
if((x > minX) && (x < maxX))
{
active.push(r.i);
}
}
}
return active;
}
this.maxZIndex = function(indices)
{
var d = this.droppables;
var l = indices.length;
var index = -1;
var maxZ = -100000000;
var curZ = 0;
while(l--)
{
curZ = parseInt(d[indices[l]].r.style.zIndex || 0);
if(curZ > maxZ)
{
maxZ = curZ;
index = indices[l];
}
}
return index;
}
this.process = function(index, draggableRoot)
{
//only perform update if a change has occured
if(this.lastIndex != index)
{
//remove previous
if(this.lastIndex != null)
{
var d = this.droppables[this.lastIndex]
var p = d.p;
var r = d.r;
if(p.hoverClass)
{
webkit_tools.removeClassName(r,p.hoverClass);
}
p.onOut();
this.lastIndex = null;
this.lastOutput = false;
}
//add new
if(index != -1)
{
var d = this.droppables[index]
var p = d.p;
var r = d.r;
if(this.hasClassNames(draggableRoot, p.accept))
{
if(p.hoverClass)
{
webkit_tools.addClassName(r,p.hoverClass);
}
p.onOver();
this.lastIndex = index;
this.lastOutput = true;
}
}
}
return this.lastOutput;
}
this.drop = function(index, r, e)
{
if(index != -1)
{
this.droppables[index].p.onDrop(r,e);
}
}
this.hasClassNames = function(r, names)
{
var l = names.length;
if(l == 0){return true}
while(l--)
{
if(webkit_tools.hasClassName(r,names[l]))
{
return true;
}
}
return false;
}
this.initialize();
}
webkit_drop = new webkit_droppables();
//Description
//webkit draggable - allows users to drag elements with their hands
var webkit_draggable = function(r, ip)
{
this.initialize = function(root, instance_props)
{
this.root = webkit_tools.$(root);
var default_props = {scroll : false, revert : false, handle : this.root, zIndex : 1000, onStart : webkit_tools.empty, onEnd : webkit_tools.empty};
this.p = webkit_tools.extend(default_props, instance_props || {});
default_props.handle = webkit_tools.$(default_props.handle);
this.prepare();
this.bindEvents();
}
this.prepare = function()
{
var rs = this.root.style;
//set position
if(webkit_tools.getCalculatedStyle(this.root,'position') != 'absolute')
{
rs.position = 'relative';
}
//set top, right, bottom, left
rs.top = rs.top || '0px';
rs.left = rs.left || '0px';
rs.right = "";
rs.bottom = "";
//set zindex;
rs.zIndex = rs.zIndex || '0';
}
this.bindEvents = function()
{
var handle = this.p.handle;
this.ts = webkit_tools.bindAsEventListener(this.touchStart, this);
this.tm = webkit_tools.bindAsEventListener(this.touchMove, this);
this.te = webkit_tools.bindAsEventListener(this.touchEnd, this);
handle.addEventListener("touchstart", this.ts, false);
handle.addEventListener("touchmove", this.tm, false);
handle.addEventListener("touchend", this.te, false);
}
this.destroy = function()
{
var handle = this.p.handle;
handle.removeEventListener("touchstart", this.ts);
handle.removeEventListener("touchmove", this.tm);
handle.removeEventListener("touchend", this.te);
}
this.set = function(key, value)
{
this.p[key] = value;
}
this.touchStart = function(event)
{
//prepare needed variables
var p = this.p;
var r = this.root;
var rs = r.style;
var t = event.targetTouches[0];
//get position of touch
touchX = t.pageX;
touchY = t.pageY;
//set base values for position of root
rs.top = this.root.style.top || '0px';
rs.left = this.root.style.left || '0px';
rs.bottom = null;
rs.right = null;
var rootP = webkit_tools.cumulativeOffset(r);
var cp = this.getPosition();
//save event properties
p.rx = cp.x;
p.ry = cp.y;
p.tx = touchX;
p.ty = touchY;
p.z = parseInt(this.root.style.zIndex);
//boost zIndex
rs.zIndex = p.zIndex;
webkit_drop.prepare();
p.onStart();
}
this.touchMove = function(event)
{
event.preventDefault();
event.stopPropagation();
//prepare needed variables
var p = this.p;
var r = this.root;
var rs = r.style;
var t = event.targetTouches[0];
if(t == null){return}
var curX = t.pageX;
var curY = t.pageY;
var delX = curX - p.tx;
var delY = curY - p.ty;
rs.left = p.rx + delX + 'px';
rs.top = p.ry + delY + 'px';
//scroll window
if(p.scroll)
{
s = this.getScroll(curX, curY);
if((s[0] != 0) || (s[1] != 0))
{
window.scrollTo(window.scrollX + s[0], window.scrollY + s[1]);
}
}
//check droppables
webkit_drop.check(curX, curY, r);
//save position for touchEnd
this.lastCurX = curX;
this.lastCurY = curY;
}
this.touchEnd = function(event)
{
var r = this.root;
var p = this.p;
var dropped = webkit_drop.finalize(this.lastCurX, this.lastCurY, r, event);
if(((p.revert) && (!dropped)) || (p.revert === 'always'))
{
//revert root
var rs = r.style;
rs.top = (p.ry + 'px');
rs.left = (p.rx + 'px');
}
r.style.zIndex = this.p.z;
this.p.onEnd();
}
this.getPosition = function()
{
var rs = this.root.style;
return {x : parseInt(rs.left || 0), y : parseInt(rs.top || 0)}
}
this.getScroll = function(pX, pY)
{
//read window variables
var sX = window.scrollX;
var sY = window.scrollY;
var wX = window.innerWidth;
var wY = window.innerHeight;
//set contants
var scroll_amount = 10; //how many pixels to scroll
var scroll_sensitivity = 100; //how many pixels from border to start scrolling from.
var delX = 0;
var delY = 0;
//process vertical y scroll
if(pY - sY < scroll_sensitivity)
{
delY = -scroll_amount;
}
else
if((sY + wY) - pY < scroll_sensitivity)
{
delY = scroll_amount;
}
//process horizontal x scroll
if(pX - sX < scroll_sensitivity)
{
delX = -scroll_amount;
}
else
if((sX + wX) - pX < scroll_sensitivity)
{
delX = scroll_amount;
}
return [delX, delY]
}
//contructor
this.initialize(r, ip);
}
//Description
//webkit_click class. manages click events for draggables
var webkit_click = function(r, ip)
{
this.initialize = function(root, instance_props)
{
var default_props = {onClick : webkit_tools.empty};
this.root = webkit_tools.$(root);
this.p = webkit_tools.extend(default_props, instance_props || {});
this.bindEvents();
}
this.bindEvents = function()
{
var root = this.root;
//bind events to local scope
this.ts = webkit_tools.bindAsEventListener(this.touchStart,this);
this.tm = webkit_tools.bindAsEventListener(this.touchMove,this);
this.te = webkit_tools.bindAsEventListener(this.touchEnd,this);
//add Listeners
root.addEventListener("touchstart", this.ts, false);
root.addEventListener("touchmove", this.tm, false);
root.addEventListener("touchend", this.te, false);
this.bound = true;
}
this.touchStart = function()
{
this.moved = false;
if(this.bound == false)
{
this.root.addEventListener("touchmove", this.tm, false);
this.bound = true;
}
}
this.touchMove = function()
{
this.moved = true;
this.root.removeEventListener("touchmove", this.tm);
this.bound = false;
}
this.touchEnd = function()
{
if(this.moved == false)
{
this.p.onClick();
}
}
this.setEvent = function(f)
{
if(typeof(f) == 'function')
{
this.p.onClick = f;
}
}
this.unbind = function()
{
var root = this.root;
root.removeEventListener("touchstart", this.ts);
root.removeEventListener("touchmove", this.tm);
root.removeEventListener("touchend", this.te);
}
//call constructor
this.initialize(r, ip);
}
If your classnames are unique, the solution is rather simple. You can change the $ function to get by class name instead of by id:
var webkit_tools =
{
//$ function - simply a more robust getElementById
$:function(e)
{
if(typeof(e) == 'string')
{
return document.getElementsByClassName(e)[0];
// return document.getElementById(e);
}
return e;
},
... snipped ...
I've verified the above solution works (dragging and dropping) on my iPhone, but again, if the classnames are not unique, some added work will be in order based on the script's current implementation.
~~~EDIT~~~
In re-reading your request, you state that there will in fact NOT be unique classnames, hence the need for some sort of "bulk" drag/drop functionality. I've modified/extended the framework to support this. You can find the source for the modified version here:
https://gist.github.com/2474416
I had to change the API slightly. The dropabble API is unchanged, so passing a classname will simply add/remove the whole list of elements matching the classname passed. The clickable/draggable API was not so easy. To avoid a harsh rewrite, I updated the initialize methods for draggable/clickable to take an element ref rather than id or classname.
Correspondingly, I added a bulk_draggable(clazzname, options) and bulk_clickable(clazzname, options) function that basically iterates over the matched elements and calls the corresponding initializers. These functions return an array of draggables/clickables (one for each matched element).
Let me know if the "new" API is unclear. I did this rather quickly and lightly tested the happy paths, but can't invest and substantial amount of time rewriting the entire script.