QueryStringBuilder,Loader and Treenode own files.

Own files for all of them.
pull/702/head
Nils Kenneweg 11 years ago
parent 600bfeb895
commit 9d925d725f
  1. 162
      app/assets/javascripts/FilterQueryStringBuilder.js
  2. 1372
      app/assets/javascripts/timelines.js
  3. 162
      app/assets/javascripts/timelines/FilterQueryStringBuilder.js
  4. 1040
      app/assets/javascripts/timelines/TimelineLoader.js
  5. 338
      app/assets/javascripts/timelines/TreeNode.js

@ -0,0 +1,162 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
// ╭───────────────────────────────────────────────────────────────╮
// │ _____ _ _ _ │
// │ |_ _(_)_ __ ___ ___| (_)_ __ ___ ___ │
// │ | | | | '_ ` _ \ / _ \ | | '_ \ / _ \/ __| │
// │ | | | | | | | | | __/ | | | | | __/\__ \ │
// │ |_| |_|_| |_| |_|\___|_|_|_| |_|\___||___/ │
// ├───────────────────────────────────────────────────────────────┤
// │ Javascript library that fetches and plots timelines for the │
// │ OpenProject timelines module. │
// ╰───────────────────────────────────────────────────────────────╯
// stricter than default
/*jshint undef:true,
eqeqeq:true,
forin:true,
immed:true,
latedef:true,
trailing: true
*/
// looser than default
/*jshint eqnull:true */
// environment and other global vars
/*jshint browser:true, devel:true*/
/*global jQuery:false, Raphael:false, Timeline:true*/
Timeline = Timeline || {};
//FilterQueryStringBuilder
jQuery.extend(Timeline, {
// ╭───────────────────────────────────────────────────────────────────╮
// │ Loading │
// ╰───────────────────────────────────────────────────────────────────╯
FilterQueryStringBuilder: (function() {
/**
* FilterQueryStringBuilder
*
* Simple serializer of query strings that satisfies OpenProject's filter
* API. Transforms hashes of desired filterings into the proper query strings.
*
* Examples:
*
* fqsb = (new FilterQueryStringBuilder({
* 'type_id': [4, 5]
* })).build(
* '/api/v2/projects/sample_project/planning_elements.json'
* );
*
* => /api/v2/projects/sample_project/planning_elements.json?f[]=type_id&op[type_id]==&v[type_id][]=4&v[type_id][]=5
*
* fqsb = (new FilterQueryStringBuilder())
* .filter({ 'type_id': [4, 5] })
* .append({ 'at_time': 1380795754 })
* .build( '/api/v2/projects/sample_project/planning_elements.json' );
*
* => /api/v2/projects/sample_project/planning_elements.json?f[]=type_id&op[type_id]==&v[type_id][]=4&v[type_id][]=5&at_time=1380795754
*/
var FilterQueryStringBuilder = function (filterHash) {
this.filterHash = filterHash || {};
this.paramsHash = {};
};
FilterQueryStringBuilder.prototype.filter = function(filters) {
this.filterHash = jQuery.extend({}, this.filterHash, filters);
return this;
};
FilterQueryStringBuilder.prototype.append = function(addition) {
this.paramsHash = jQuery.extend({}, this.paramsHash, addition);
return this;
};
FilterQueryStringBuilder.prototype.buildMetaDataForKey = function(key) {
this.queryStringParts.push({name: 'f[]', value: key},
{name: 'op[' + key + ']', value: '='});
};
FilterQueryStringBuilder.prototype.prepareFilterDataForKeyAndValue = function(key, value) {
this.queryStringParts.push({name: 'v[' + key + '][]', value: value});
};
FilterQueryStringBuilder.prototype.prepareAdditionalQueryData = function(key, value) {
this.queryStringParts.push({name: key, value: value});
};
FilterQueryStringBuilder.prototype.prepareFilterDataForKeyAndArrayOfValues = function(key, value) {
jQuery.each(value, jQuery.proxy( function(i, e) {
this.prepareFilterDataForKeyAndValue(key, e);
}, this));
};
FilterQueryStringBuilder.prototype.buildFilterDataForValue = function(key, value) {
if (value instanceof Array) {
this.prepareFilterDataForKeyAndArrayOfValues(key, value);
} else {
this.prepareFilterDataForKeyAndValue(key, value);
}
};
FilterQueryStringBuilder.prototype.registerKeyAndValue = function(key, value) {
this.buildMetaDataForKey(key);
this.buildFilterDataForValue(key, value);
};
FilterQueryStringBuilder.prototype.prepareQueryStringParts = function() {
this.queryStringParts = [];
jQuery.each(this.filterHash, jQuery.proxy(this.registerKeyAndValue, this));
jQuery.each(this.paramsHash, jQuery.proxy(this.prepareAdditionalQueryData, this));
};
FilterQueryStringBuilder.prototype.buildQueryStringFromQueryStringParts = function(url) {
return jQuery.map(this.queryStringParts, function(e, i) {
return e.name + "=" + encodeURIComponent(e.value);
}).join('&');
};
FilterQueryStringBuilder.prototype.buildUrlFromQueryStringParts = function(url) {
var resultUrl = url;
resultUrl += "?";
resultUrl += this.buildQueryStringFromQueryStringParts();
return resultUrl;
};
FilterQueryStringBuilder.prototype.build = function(url) {
this.prepareQueryStringParts();
return this.buildUrlFromQueryStringParts(url);
};
return FilterQueryStringBuilder;
})(),
});

File diff suppressed because it is too large Load Diff

@ -0,0 +1,162 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
// ╭───────────────────────────────────────────────────────────────╮
// │ _____ _ _ _ │
// │ |_ _(_)_ __ ___ ___| (_)_ __ ___ ___ │
// │ | | | | '_ ` _ \ / _ \ | | '_ \ / _ \/ __| │
// │ | | | | | | | | | __/ | | | | | __/\__ \ │
// │ |_| |_|_| |_| |_|\___|_|_|_| |_|\___||___/ │
// ├───────────────────────────────────────────────────────────────┤
// │ Javascript library that fetches and plots timelines for the │
// │ OpenProject timelines module. │
// ╰───────────────────────────────────────────────────────────────╯
// stricter than default
/*jshint undef:true,
eqeqeq:true,
forin:true,
immed:true,
latedef:true,
trailing: true
*/
// looser than default
/*jshint eqnull:true */
// environment and other global vars
/*jshint browser:true, devel:true*/
/*global jQuery:false, Raphael:false, Timeline:true*/
Timeline = Timeline || {};
//FilterQueryStringBuilder
jQuery.extend(Timeline, {
// ╭───────────────────────────────────────────────────────────────────╮
// │ Loading │
// ╰───────────────────────────────────────────────────────────────────╯
FilterQueryStringBuilder: (function() {
/**
* FilterQueryStringBuilder
*
* Simple serializer of query strings that satisfies OpenProject's filter
* API. Transforms hashes of desired filterings into the proper query strings.
*
* Examples:
*
* fqsb = (new FilterQueryStringBuilder({
* 'type_id': [4, 5]
* })).build(
* '/api/v2/projects/sample_project/planning_elements.json'
* );
*
* => /api/v2/projects/sample_project/planning_elements.json?f[]=type_id&op[type_id]==&v[type_id][]=4&v[type_id][]=5
*
* fqsb = (new FilterQueryStringBuilder())
* .filter({ 'type_id': [4, 5] })
* .append({ 'at_time': 1380795754 })
* .build( '/api/v2/projects/sample_project/planning_elements.json' );
*
* => /api/v2/projects/sample_project/planning_elements.json?f[]=type_id&op[type_id]==&v[type_id][]=4&v[type_id][]=5&at_time=1380795754
*/
var FilterQueryStringBuilder = function (filterHash) {
this.filterHash = filterHash || {};
this.paramsHash = {};
};
FilterQueryStringBuilder.prototype.filter = function(filters) {
this.filterHash = jQuery.extend({}, this.filterHash, filters);
return this;
};
FilterQueryStringBuilder.prototype.append = function(addition) {
this.paramsHash = jQuery.extend({}, this.paramsHash, addition);
return this;
};
FilterQueryStringBuilder.prototype.buildMetaDataForKey = function(key) {
this.queryStringParts.push({name: 'f[]', value: key},
{name: 'op[' + key + ']', value: '='});
};
FilterQueryStringBuilder.prototype.prepareFilterDataForKeyAndValue = function(key, value) {
this.queryStringParts.push({name: 'v[' + key + '][]', value: value});
};
FilterQueryStringBuilder.prototype.prepareAdditionalQueryData = function(key, value) {
this.queryStringParts.push({name: key, value: value});
};
FilterQueryStringBuilder.prototype.prepareFilterDataForKeyAndArrayOfValues = function(key, value) {
jQuery.each(value, jQuery.proxy( function(i, e) {
this.prepareFilterDataForKeyAndValue(key, e);
}, this));
};
FilterQueryStringBuilder.prototype.buildFilterDataForValue = function(key, value) {
if (value instanceof Array) {
this.prepareFilterDataForKeyAndArrayOfValues(key, value);
} else {
this.prepareFilterDataForKeyAndValue(key, value);
}
};
FilterQueryStringBuilder.prototype.registerKeyAndValue = function(key, value) {
this.buildMetaDataForKey(key);
this.buildFilterDataForValue(key, value);
};
FilterQueryStringBuilder.prototype.prepareQueryStringParts = function() {
this.queryStringParts = [];
jQuery.each(this.filterHash, jQuery.proxy(this.registerKeyAndValue, this));
jQuery.each(this.paramsHash, jQuery.proxy(this.prepareAdditionalQueryData, this));
};
FilterQueryStringBuilder.prototype.buildQueryStringFromQueryStringParts = function(url) {
return jQuery.map(this.queryStringParts, function(e, i) {
return e.name + "=" + encodeURIComponent(e.value);
}).join('&');
};
FilterQueryStringBuilder.prototype.buildUrlFromQueryStringParts = function(url) {
var resultUrl = url;
resultUrl += "?";
resultUrl += this.buildQueryStringFromQueryStringParts();
return resultUrl;
};
FilterQueryStringBuilder.prototype.build = function(url) {
this.prepareQueryStringParts();
return this.buildUrlFromQueryStringParts(url);
};
return FilterQueryStringBuilder;
})(),
});

File diff suppressed because it is too large Load Diff

@ -0,0 +1,338 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
// ╭───────────────────────────────────────────────────────────────╮
// │ _____ _ _ _ │
// │ |_ _(_)_ __ ___ ___| (_)_ __ ___ ___ │
// │ | | | | '_ ` _ \ / _ \ | | '_ \ / _ \/ __| │
// │ | | | | | | | | | __/ | | | | | __/\__ \ │
// │ |_| |_|_| |_| |_|\___|_|_|_| |_|\___||___/ │
// ├───────────────────────────────────────────────────────────────┤
// │ Javascript library that fetches and plots timelines for the │
// │ OpenProject timelines module. │
// ╰───────────────────────────────────────────────────────────────╯
// stricter than default
/*jshint undef:true,
eqeqeq:true,
forin:true,
immed:true,
latedef:true,
trailing: true
*/
// looser than default
/*jshint eqnull:true */
// environment and other global vars
/*jshint browser:true, devel:true*/
/*global jQuery:false, Raphael:false, Timeline:true*/
Timeline = Timeline || {};
//Treenode
jQuery.extend(Timeline, {
TreeNode: {
payload: undefined,
parentNode: undefined,
childNodes: undefined,
expanded: false,
totalCount: 0,
projectCount: 0,
getData: function() {
return this.payload;
},
setData: function(data) {
this.payload = data;
return this;
},
appendChild: function(node) {
if (!this.childNodes) {
this.childNodes = [node];
} else {
this.childNodes.push(node);
}
node.parentNode = this;
return node.parentNode;
},
removeChild: function(node) {
var result;
jQuery.each(this.childNodes, function(i, e) {
if (node === e) {
result = node;
}
});
return result;
},
hasChildren: function() {
return this.childNodes && this.childNodes.length > 0;
},
children: function() {
return this.childNodes;
},
root: function() {
if (this.parentNode) {
return this.parentNode.root();
} else {
return this;
}
},
isExpanded: function() {
return this.expanded;
},
setExpand: function(state) {
this.expanded = state;
return this.expanded;
},
expand: function() {
return this.setExpand(true);
},
collapse: function() {
return this.setExpand(false);
},
toggle: function() {
return this.setExpand(!this.expanded);
},
setExpandedAll: function(state) {
if (!this.hasChildren()) {
return;
}
this.setExpand(state);
jQuery.each(this.children(), function(i, e) {
e.setExpandedAll(state);
});
},
expandAll: function() {
return this.setExpandedAll(true);
},
collapseAll: function() {
return this.setExpandedAll(false);
},
setDOMElement: function(element) {
this.dom_element = element;
},
getDOMElement: function() {
return this.dom_element;
},
iterateWithChildren: function(callback, options) {
var root = this.root();
var self = this;
var timeline;
var filtered_out, hidden;
var children = this.children();
var has_children = children !== undefined;
// there might not be any payload, due to insufficient rights and
// the fact that some user with more rights originally created the
// report.
if (root.payload === undefined) {
// FLAG raise some flag indicating that something is
// wrong/missing.
return this;
}
timeline = root.payload.timeline;
hidden = this.payload.hide();
filtered_out = this.payload.filteredOut();
options = options || {indent: 0, index: 0, projects: 0};
// ╭─────────────────────────────────────────────────────────╮
// │ The hide_other_group flag is an option that cuases │
// │ iteration to stop when the "other" group, i.e., │
// │ everything that is not otherwise grouped, is reached. │
// │ This effectively hides that group. │
// ╰─────────────────────────────────────────────────────────╯
// the "other" group is reached when we are dealing with a
// grouping timeline, the current payload is a project, not root,
// but on level 0, and the first level grouping is 0.
if (timeline.options.hide_other_group &&
timeline.isGrouping() &&
this.payload.is(Timeline.Project) &&
this !== root &&
options.indent === 0 &&
this.payload.getFirstLevelGrouping() === 0) {
return;
}
if (this === root) {
options = jQuery.extend({}, {indent: 0, index: 0, projects: 0, traverseCollapsed: false}, options);
}
if (this === root && timeline.options.hide_tree_root === true) {
// ╭───────────────────────────────────────────────────────╮
// │ There used to be a requirement that disabled planning │
// │ elements in root when root should be hidden. That │
// │ requirement was inverted and it is now desired to │
// │ show all such planning elements on the root level of │
// │ the tree. │
// ╰───────────────────────────────────────────────────────╯
if (has_children) {
jQuery.each(children, function(i, e) {
e.iterateWithChildren(callback, options);
});
}
} else {
// ╭───────────────────────────────────────────────────────╮
// │ There is a requirement that states that filter status │
// │ should no longer be inherited. The callback therefore │
// │ is only invoked when payload is not filtered out. The │
// │ same is true for incrementing the projects and index │
// │ count. │
// ╰───────────────────────────────────────────────────────╯
if (!filtered_out && !hidden) {
if (callback) {
callback.call(this, this, options.indent, options.index);
}
if (this.payload.is(Timeline.Project)) {
options.projects++;
}
options.index++;
}
// ╭───────────────────────────────────────────────────────╮
// │ There is a requirement that states that if the │
// │ current node is closed, children that are projects │
// │ should be displayed anyway, and only children that │
// │ are planning elements should be removed from the │
// │ view. Beware, this only works as long as there are no │
// │ projects that are children of planning elements. │
// ╰───────────────────────────────────────────────────────╯
// if there are children, loop over them, independently of
// current node expansion state.
if (has_children) {
options.indent++;
jQuery.each(children, function(i, child) {
// ╭───────────────────────────────────────────────────╮
// │ Now, if the node, the children of which we │
// │ are looping over, was expanded, iterate │
// │ over its children, recursively. Do the same │
// │ if the iteration was configured with the │
// │ traverseCollapsed flag. Last but not least, if │
// │ the current child is a project, iterate over it │
// │ only if indentation is not too deep. │
// ╰───────────────────────────────────────────────────╯
if (options.traverseCollapsed ||
self.isExpanded() ||
child.payload.is(Timeline.Project)) {
//do we wan to inherit the hidden status from projects to planning elements?
if (!hidden || child.payload.is(Timeline.Project)) {
if (!(options.indent > 1 && child.payload.is(Timeline.Project))) {
child.iterateWithChildren(callback, options);
}
}
}
});
options.indent--;
}
}
if (this === root) {
this.totalCount = options.index;
this.projectCount = options.projects;
}
return this;
},
// ╭───────────────────────────────────────────────────╮
// │ The following methods are supposed to be called │
// │ from the root level of the tree, but do │
// │ gracefully retrieve the root if called from │
// │ anywhere else. │
// ╰───────────────────────────────────────────────────╯
expandTo: function(level) {
var root = this.root();
var i = 0, expandables = [root];
var expand = function (i,e) { return e.expand(); };
var children;
var j, c;
if (level === undefined) {
// "To infinity ... and beyond!" - Buzz Lightyear.
level = Infinity;
}
// collapse all, and expand only as much as is enabled by default.
root.collapseAll();
while (i++ < level && expandables.length > 0) {
jQuery.each(expandables, expand);
children = [];
for (j = 0; j < expandables.length; j++) {
c = expandables[j].children();
if (c) {
children = children.concat(c);
}
}
expandables = children;
}
return level;
},
numberOfProjects: function() {
return this.getRootProperty('projectCount');
},
numberOfPlanningElements: function() {
return this.getRootProperty('totalCount') -
this.getRootProperty('projectCount');
},
height: function() {
return this.getRootProperty('totalCount');
},
getRootProperty: function(property) {
var root = this.root();
this.iterateWithChildren();
return root[property];
},
containsProjects: function() {
return this.numberOfProjects() !== 0;
},
containsPlanningElements: function() {
return this.numberOfPlanningElements() !== 0;
}
},
});
Loading…
Cancel
Save