//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2014 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.
//++
angular.module('openproject.timelines.models')
.factory('Timeline', ['Constants', 'TreeNode', 'UI', 'Color', 'HistoricalPlanningElement', 'PlanningElement', 'PlanningElementType', 'ProjectType', 'Project', 'ProjectAssociation', 'Reporting', 'CustomField', 'CustomFieldHelper', function(Constants, TreeNode, UI, Color, HistoricalPlanningElement, PlanningElement, PlanningElementType, ProjectType, Project, ProjectAssociation, Reporting, CustomField, CustomFieldHelper) {
Timeline = {};
// model mix ins
angular.extend(Timeline, Constants);
angular.extend(Timeline, UI);
//startup
angular.extend(Timeline, {
instances: [],
create: function(options) {
if (!options) {
throw new Error('No configuration options given');
}
this.options = options;
this.extendOptions();
this.instances = [];
var timeline = Object.create(Timeline);
// some private fields.
timeline.listeners = [];
timeline.data = {};
Timeline.instances.push(timeline);
return timeline;
},
extendOptions: function() {
this.options = jQuery.extend({}, this.defaults, this.options);
if (this.options.username) {
this.ajax_defaults.username = this.options.username;
}
if (this.options.password) {
this.ajax_defaults.password = this.options.password;
}
if (this.options.api_key) {
this.ajax_defaults.headers = {
'X-ChiliProject-API-Key': this.options.api_key,
'X-OpenProject-API-Key': this.options.api_key,
'X-Redmine-API-Key': this.options.api_key
};
}
// we're hiding the root if there is a grouping.
this.options.hide_tree_root = this.isGrouping();
},
get: function(n) {
if (typeof n !== "number") {
n = 0;
}
return this.instances[n];
},
isInstance: function(n) {
return (n === undefined) ?
Timeline.instances.indexOf(this) :
this === Timeline.get(n);
},
isGrouping: function() {
if ((this.options.grouping_one_enabled === 'yes' &&
this.options.grouping_one_selection !== undefined) ||
(this.options.grouping_two_enabled === 'yes' &&
this.options.grouping_two_selection !== undefined)) {
return true;
} else {
return false;
}
},
isComparing: function() {
return ((this.options.comparison !== undefined) &&
(this.options.comparison !== 'none'));
},
comparisonCurrentTime: function() {
var value;
if (!this.isComparing()) {
return undefined;
}
if (this.options.comparison === 'historical') {
value = this.options.compare_to_historical_two;
}
else {
// default is no (undefined) current time, which corresponds to today.
return undefined;
}
return +Date.parse(value) / 1000;
},
calculateTimeFilter: function () {
if (!this.frameSet) {
if (this.options.planning_element_time === "absolute") {
this.frameStart = Date.parse(this.options.planning_element_time_absolute_one);
this.frameEnd = Date.parse(this.options.planning_element_time_absolute_two);
} else if (this.options.planning_element_time === "relative") {
var startR = parseInt(this.options.planning_element_time_relative_one, 10);
var endR = parseInt(this.options.planning_element_time_relative_two, 10);
if (!isNaN(startR)) {
this.frameStart = Date.now();
switch (this.options.planning_element_time_relative_one_unit[0]) {
case "0":
this.frameStart.add(-1 * startR).days();
break;
case "1":
this.frameStart.add(-1 * startR).weeks();
break;
case "2":
this.frameStart.add(-1 * startR).months();
break;
}
}
if (!isNaN(endR)) {
this.frameEnd = Date.now();
switch (this.options.planning_element_time_relative_two_unit[0]) {
case "0":
this.frameEnd.add(endR).days();
break;
case "1":
this.frameEnd.add(endR).weeks();
break;
case "2":
this.frameEnd.add(endR).months();
break;
}
}
}
this.frameSet = true;
}
},
inTimeFilter: function (start, end) {
this.calculateTimeFilter();
if (!start && !end) {
return false;
}
if (!start) {
start = end;
}
if (!end) {
end = start;
}
if (this.frameStart) {
if (start < this.frameStart && end < this.frameStart) {
return false;
}
}
if (this.frameEnd) {
if (start > this.frameEnd && end > this.frameEnd) {
return false;
}
}
return true;
},
verticalPlanningElementIds: function() {
return this.options.vertical_planning_elements ?
jQuery.map(
this.options.vertical_planning_elements.split(/\,/),
function(a) {
try {
return parseInt(a.match(/\s*\*?(\d*)\s*/)[1], 10);
} catch (e) {
return;
}
}
) : [];
},
comparisonTarget: function() {
var result, value, unit;
if (!this.isComparing()) {
return undefined;
}
switch (this.options.comparison) {
case 'relative':
result = new Date();
value = Timeline.pnum(this.options.compare_to_relative);
unit = Timeline.pnum(this.options.compare_to_relative_unit[0]);
switch (unit) {
case 0:
return Math.floor(result.add(-value).days() / 1000);
case 1:
return Math.floor(result.add(-value).weeks() / 1000);
case 2:
return Math.floor(result.add(-value).months() / 1000);
default:
return this.die(I18n.t('js.timelines.errors.report_comparison'));
}
break; // to please jslint
case 'absolute':
value = this.options.compare_to_absolute;
break;
case 'historical':
value = this.options.compare_to_historical_one;
break;
default:
return this.die(I18n.t('js.timelines.errors.report_comparison'));
}
return +Date.parse(value)/1000;
},
registerTimelineContainer: function(uiRoot) {
this.uiRoot = uiRoot;
this.registerDrawPaper();
},
checkPrerequisites: function() {
if (jQuery === undefined) {
throw new Error('jQuery seems to be missing (jQuery is undefined)');
} else if (jQuery().slider === undefined) {
throw new Error('jQuery UI seems to be missing (jQuery().slider is undefined)');
} else if ((1).month === undefined) {
throw new Error('date.js seems to be missing ((1).month is undefined)');
}
return true;
},
getTimelineLoaderOptions: function() {
return {
api_prefix : this.options.api_prefix,
url_prefix : this.options.url_prefix,
project_prefix : this.options.project_prefix,
planning_element_prefix : this.options.planning_element_prefix,
timeline_id : this.options.timeline_id,
project_id : this.options.project_id,
project_types : this.options.project_types,
project_statuses : this.options.project_status,
project_responsibles : this.options.project_responsibles,
project_parents : this.options.parents,
planning_element_types : this.options.planning_element_types,
planning_element_assignee : this.options.planning_element_assignee,
planning_element_responsibles : this.options.planning_element_responsibles,
custom_fields : this.options.custom_fields,
planning_element_status : this.options.planning_element_status,
grouping_one : (this.options.grouping_one_enabled ? this.options.grouping_one_selection : undefined),
grouping_two : (this.options.grouping_two_enabled ? this.options.grouping_two_selection : undefined),
ajax_defaults : this.ajax_defaults,
current_time : this.comparisonCurrentTime(),
target_time : this.comparisonTarget(),
include_planning_elements : this.verticalPlanningElementIds()
};
},
die: function(error, classes) {
var message = (typeof error === 'string') ? error :
I18n.t('js.timelines.errors.report_epicfail'); // + '
' + error.message;
classes = classes || 'flash error';
this.warn(message, classes);
// assume this won't happen anymore.
this.onLoadComplete = function() {};
if (console && console.log) {
console.log(error.stack);
}
throw error;
},
warn: function(message, classes, viewCallback) {
var root = this.getUiRoot();
window.setTimeout(function() {
// generate and display the error message.
var warning = jQuery('