Before Width: | Height: | Size: 139 B After Width: | Height: | Size: 139 B |
Before Width: | Height: | Size: 67 B After Width: | Height: | Size: 67 B |
Before Width: | Height: | Size: 1022 B After Width: | Height: | Size: 1022 B |
Before Width: | Height: | Size: 64 B After Width: | Height: | Size: 64 B |
Before Width: | Height: | Size: 67 B After Width: | Height: | Size: 67 B |
Before Width: | Height: | Size: 67 B After Width: | Height: | Size: 67 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 54 B After Width: | Height: | Size: 54 B |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 139 B |
After Width: | Height: | Size: 67 B |
After Width: | Height: | Size: 1022 B |
After Width: | Height: | Size: 64 B |
After Width: | Height: | Size: 67 B |
After Width: | Height: | Size: 67 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 54 B |
After Width: | Height: | Size: 3.7 KiB |
@ -0,0 +1,159 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009 Charles Cordingley (www.cordinc.com) |
||||||
|
* |
||||||
|
* Permission to use, copy, modify, and distribute this software for any |
||||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||||
|
* copyright notice and this permission notice appear in all copies. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* |
||||||
|
* cordinc_tooltip.js, v1.0.2 - 27 August 2008 |
||||||
|
* For help see www.cordinc.com/projects/tooltips.html |
||||||
|
*/ |
||||||
|
var Tooltip = Class.create({ |
||||||
|
initialize: function(target, tooltip) { |
||||||
|
var options = Object.extend({ |
||||||
|
start_effect: function(element) {}, |
||||||
|
end_effect: function(element) {}, |
||||||
|
zindex: 1000, |
||||||
|
offset: {x:0, y:0}, |
||||||
|
hook: {target:'topRight', tip:'bottomLeft'}, |
||||||
|
trigger: false,
|
||||||
|
DOM_location: false, |
||||||
|
className: false, |
||||||
|
delay: {} |
||||||
|
}, arguments[2] || {}); |
||||||
|
this.target = $(target); |
||||||
|
this.show_at = (options.show_at_id !== undefined) ? $(options.show_at_id) : undefined |
||||||
|
this.tooltip = $(tooltip); |
||||||
|
this.options = options; |
||||||
|
this.event_target = this.options.trigger?$(this.options.trigger):this.target; |
||||||
|
|
||||||
|
if (this.options.className) { |
||||||
|
this.tooltip.addClassName(this.options.className); |
||||||
|
} |
||||||
|
this.tooltip.hide(); |
||||||
|
this.display=false; |
||||||
|
|
||||||
|
this.mouse_over = this.displayTooltip.bindAsEventListener(this); |
||||||
|
this.mouse_out = this.removeTooltip.bindAsEventListener(this); |
||||||
|
this.event_target.observe("mouseover", this.mouse_over); |
||||||
|
this.event_target.observe("mouseout", this.mouse_out); |
||||||
|
}, |
||||||
|
|
||||||
|
displayTooltip: function(event){ |
||||||
|
event.stop(); |
||||||
|
|
||||||
|
if (this.display) {return;} |
||||||
|
if (this.options.delay.start) { |
||||||
|
var self = this; |
||||||
|
this.timer_id = setTimeout(function(){self.timer_id = false; self.showTooltip(event);}, this.options.delay.start*1000); |
||||||
|
} else { |
||||||
|
this.showTooltip(event); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
showTooltip: function(event) { |
||||||
|
var show_at = (this.show_at !== undefined) ? this.show_at : this.target |
||||||
|
this.display=true; |
||||||
|
position = this.positionTooltip(event); |
||||||
|
|
||||||
|
this.clone = this.tooltip.cloneNode(true); |
||||||
|
parentId = this.options.DOM_location?$(this.options.DOM_location.parentId):show_at.parentNode; |
||||||
|
successorId = this.options.DOM_location?$(this.options.DOM_location.successorId):show_at; |
||||||
|
parentId.insertBefore(this.clone, successorId); |
||||||
|
|
||||||
|
this.clone.setStyle({ |
||||||
|
position: 'absolute', |
||||||
|
top: position.top + "px", |
||||||
|
left: position.left + "px", |
||||||
|
display: "inline", |
||||||
|
zIndex:this.options.zindex, |
||||||
|
/* fix for ur dashboard */ |
||||||
|
visibility: 'visible', |
||||||
|
width: "400px" |
||||||
|
}); |
||||||
|
|
||||||
|
if (this.options.start_effect) { |
||||||
|
this.options.start_effect(this.clone); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
positionTooltip: function(event) { |
||||||
|
target_position = this.target.cumulativeOffset(); |
||||||
|
|
||||||
|
tooltip_dimensions = this.tooltip.getDimensions(); |
||||||
|
target_dimensions = this.target.getDimensions(); |
||||||
|
|
||||||
|
this.positionModify(target_position, target_dimensions, this.options.hook.target, 1); |
||||||
|
this.positionModify(target_position, tooltip_dimensions, this.options.hook.tip, -1); |
||||||
|
|
||||||
|
target_position.top += this.options.offset.y; |
||||||
|
target_position.left += this.options.offset.x; |
||||||
|
|
||||||
|
return target_position;
|
||||||
|
}, |
||||||
|
|
||||||
|
positionModify: function(position, box, corner, neg) { |
||||||
|
if (corner == 'topRight') { |
||||||
|
position.left += box.width*neg; |
||||||
|
} else if (corner == 'topLeft') { |
||||||
|
} else if (corner == 'bottomLeft') { |
||||||
|
position.top += box.height*neg; |
||||||
|
} else if (corner == 'bottomRight') { |
||||||
|
position.top += box.height*neg; |
||||||
|
position.left += box.width*neg; |
||||||
|
} else if (corner == 'topMid') { |
||||||
|
position.left += (box.width/2)*neg; |
||||||
|
} else if (corner == 'leftMid') { |
||||||
|
position.top += (box.height/2)*neg; |
||||||
|
} else if (corner == 'bottomMid') { |
||||||
|
position.top += box.height*neg; |
||||||
|
position.left += (box.width/2)*neg; |
||||||
|
} else if (corner == 'rightMid') { |
||||||
|
position.top += (box.height/2)*neg; |
||||||
|
position.left += box.width*neg; |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
removeTooltip: function(event) { |
||||||
|
if (this.timer_id) { |
||||||
|
clearTimeout(this.timer_id); |
||||||
|
this.timer_id = false; |
||||||
|
return; |
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.end_effect) { |
||||||
|
this.options.end_effect(this.clone); |
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.delay.end) { |
||||||
|
var self = this; |
||||||
|
setTimeout(function(){self.clearTooltip();}, this.options.delay.end*1000); |
||||||
|
} else { |
||||||
|
this.clearTooltip(); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
clearTooltip: function() { |
||||||
|
if (this.clone !== undefined && this.clone !== null) { |
||||||
|
this.clone.remove(); |
||||||
|
this.clone = null; |
||||||
|
this.display=false; |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
destroy: function() { |
||||||
|
this.event_target.stopObserving("mouseover", this.mouse_over); |
||||||
|
this.event_target.stopObserving("mouseout", this.mouse_out); |
||||||
|
this.clearTooltip(); |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,72 @@ |
|||||||
|
/*jslint white: false, nomen: true, devel: true, on: true, debug: false, evil: true, onevar: false, browser: true, white: false, indent: 2 */ |
||||||
|
/*global window, $, $$, Reporting, Element */ |
||||||
|
|
||||||
|
window.Reporting = { |
||||||
|
source: ($$("head")[0].select("script[src*='reporting.js']")[0].src), |
||||||
|
|
||||||
|
require: function (libraryName) { |
||||||
|
var jsName = Reporting.source.replace("reporting.js", "reporting/" + libraryName + ".js"); |
||||||
|
try { |
||||||
|
// inserting via DOM fails in Safari 2.0, so brute force approach
|
||||||
|
document.write('<script type="text/javascript" src="' + jsName + '"><\/script>'); |
||||||
|
} catch (e) { |
||||||
|
// for xhtml+xml served content, fall back to DOM methods
|
||||||
|
var script = document.createElement('script'); |
||||||
|
script.type = 'text/javascript'; |
||||||
|
script.src = jsName; |
||||||
|
document.getElementsByTagName('head')[0].appendChild(script); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
onload: function (func) { |
||||||
|
document.observe("dom:loaded", func); |
||||||
|
}, |
||||||
|
|
||||||
|
flash: function (string, type) { |
||||||
|
if (type === undefined) { |
||||||
|
type = "error"; |
||||||
|
} |
||||||
|
|
||||||
|
if ($("flash_" + type) !== null) { |
||||||
|
$("flash_" + type).remove(); |
||||||
|
} |
||||||
|
|
||||||
|
var flash = new Element('div', { |
||||||
|
'id': 'flash_' + type, |
||||||
|
'class': 'flash ' + type |
||||||
|
}).update(new Element('a', { |
||||||
|
'href': '#' |
||||||
|
}).update(string)); |
||||||
|
|
||||||
|
$("content").insert({top: flash}); |
||||||
|
$$("#flash_" + type + " a")[0].focus(); |
||||||
|
}, |
||||||
|
|
||||||
|
clearFlash: function () { |
||||||
|
$$('div[id^=flash]').each(function (oldMsg) { |
||||||
|
oldMsg.remove(); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
fireEvent: function (element, event) { |
||||||
|
var evt; |
||||||
|
if (document.createEventObject) { |
||||||
|
// dispatch for IE
|
||||||
|
evt = document.createEventObject(); |
||||||
|
return element.fireEvent('on' + event, evt); |
||||||
|
} else { |
||||||
|
// dispatch for firefox + others
|
||||||
|
evt = document.createEvent("HTMLEvents"); |
||||||
|
evt.initEvent(event, true, true); // event type,bubbling,cancelable
|
||||||
|
return !element.dispatchEvent(evt); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Reporting.require("filters"); |
||||||
|
Reporting.require("group_bys"); |
||||||
|
Reporting.require("restore_query"); |
||||||
|
Reporting.require("controls"); |
||||||
|
Reporting.require("prototype_progress_bar"); |
||||||
|
Reporting.require("progressbar"); |
||||||
|
|
@ -0,0 +1,192 @@ |
|||||||
|
/*jslint white: false, nomen: true, devel: true, on: true, debug: false, evil: true, onevar: false, browser: true, white: false, indent: 2 */ |
||||||
|
/*global window, $, $$, Reporting, Effect, Ajax, Element, selectAllOptions, Form */ |
||||||
|
|
||||||
|
Reporting.Controls = { |
||||||
|
query_name_editor: function (target_id) { |
||||||
|
var target = $(target_id); |
||||||
|
var isPublic = target.getAttribute("data-is_public") === "true"; |
||||||
|
var updateUrl = target.getAttribute("data-update-url"); |
||||||
|
var translations = target.getAttribute("data-translations"); |
||||||
|
if (translations.isJSON()) { |
||||||
|
translations = translations.evalJSON(true); |
||||||
|
} |
||||||
|
if (translations === undefined) { |
||||||
|
translations = {}; |
||||||
|
} |
||||||
|
if (translations.rename === undefined) { |
||||||
|
translations.rename = 'ok'; |
||||||
|
} |
||||||
|
if (translations.cancel === undefined) { |
||||||
|
translations.cancel = 'cancel'; |
||||||
|
} |
||||||
|
if (translations.saving === undefined) { |
||||||
|
translations.saving = 'Saving...'; |
||||||
|
} |
||||||
|
if (translations.loading === undefined) { |
||||||
|
translations.loading = 'Loading...'; |
||||||
|
} |
||||||
|
if (translations.clickToEdit === undefined) { |
||||||
|
translations.loading = 'Click to edit'; |
||||||
|
} |
||||||
|
|
||||||
|
var editor = new Ajax.InPlaceEditor(target_id, updateUrl, { |
||||||
|
callback: function (form, value) { |
||||||
|
return 'query_name=' + encodeURIComponent(value); |
||||||
|
}, |
||||||
|
okControl: 'button', |
||||||
|
cancelControl: 'button', |
||||||
|
externalControl: 'query-name-edit-button', |
||||||
|
okText: translations.rename, |
||||||
|
cancelText: translations.cancel, |
||||||
|
savingText: translations.saving, |
||||||
|
loadingText: translations.loading, |
||||||
|
clickToEditText: translations.clickToEdit, |
||||||
|
onFailure: function (editor, response) { |
||||||
|
Reporting.flash(response.responseText); |
||||||
|
}, |
||||||
|
onComplete: function () { |
||||||
|
Reporting.Controls.update_report_lists(); |
||||||
|
} |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
toggle_delete_form: function (e) { |
||||||
|
var offset = $('query-icon-delete').positionedOffset().left; |
||||||
|
$('delete_form').setStyle("left: " + offset + "px").toggle(); |
||||||
|
e.preventDefault(); |
||||||
|
}, |
||||||
|
|
||||||
|
toggle_save_as_form: function (e) { |
||||||
|
var offset = $('query-icon-save-as').positionedOffset().left; |
||||||
|
$('save_as_form').setStyle("left: " + offset + "px").toggle(); |
||||||
|
e.preventDefault(); |
||||||
|
}, |
||||||
|
|
||||||
|
clear_query: function (e) { |
||||||
|
Reporting.Filters.clear(); |
||||||
|
Reporting.GroupBys.clear(); |
||||||
|
e.preventDefault(); |
||||||
|
}, |
||||||
|
|
||||||
|
send_settings_data: function (targetUrl, callback, failureCallback) { |
||||||
|
if (failureCallback === undefined) { |
||||||
|
failureCallback = Reporting.Controls.default_failure_callback; |
||||||
|
} |
||||||
|
Reporting.clearFlash(); |
||||||
|
new Ajax.Request( |
||||||
|
targetUrl, |
||||||
|
{ asynchronous: true, |
||||||
|
evalScripts: true, |
||||||
|
postBody: Reporting.Controls.serialize_settings_form(), |
||||||
|
onSuccess: callback, |
||||||
|
onFailure: failureCallback }); |
||||||
|
}, |
||||||
|
|
||||||
|
serialize_settings_form: function() { |
||||||
|
var ret_str, grouping_str; |
||||||
|
ret_str = Form.serialize('query_form'); |
||||||
|
grouping_str = $w('rows columns').inject('', function(grouping, type) { |
||||||
|
return grouping + $('group_by_' + type).select('.group_by_element').map(function(group_by) { |
||||||
|
return 'groups[' + type + '][]=' + group_by.readAttribute('data-group-by'); |
||||||
|
}).inject('', function(all_group_str, group_str) { |
||||||
|
return all_group_str + '&' + group_str; |
||||||
|
}); |
||||||
|
}); |
||||||
|
if (grouping_str.length > 0) { |
||||||
|
ret_str += grouping_str; |
||||||
|
} |
||||||
|
return ret_str; |
||||||
|
}, |
||||||
|
|
||||||
|
attach_settings_callback: function (element, callback) { |
||||||
|
if (element === null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
failureCallback = function (response) { |
||||||
|
$('result-table').update(""); |
||||||
|
Reporting.Controls.default_failure_callback(response); |
||||||
|
}; |
||||||
|
element.observe("click", function (e) { |
||||||
|
Reporting.Controls.send_settings_data(this.getAttribute("data-target"), callback, failureCallback); |
||||||
|
e.preventDefault(); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
observe_click: function (element_id, callback) { |
||||||
|
var el = $(element_id); |
||||||
|
if (el !== null && el !== undefined) { |
||||||
|
el.observe("click", callback); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
update_result_table: function (response) { |
||||||
|
$('result-table').update(response.responseText); |
||||||
|
Reporting.Progress.confirm_question(); |
||||||
|
}, |
||||||
|
|
||||||
|
default_failure_callback: function (response) { |
||||||
|
if (response.status >= 400 && response.status < 500) { |
||||||
|
Reporting.flash(response.responseText); |
||||||
|
Reporting.Progress.abort(); |
||||||
|
} else { |
||||||
|
Reporting.flash(Reporting._LA["RESPONSE_ERROR"]); |
||||||
|
Reporting.Progress.abort(); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
update_report_lists: function () { |
||||||
|
$$(".report_list").each(function (list) { |
||||||
|
Reporting.Controls.update_report_list(list); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
update_report_list: function (list) { |
||||||
|
var url = $(list).readAttribute("data-update-url"); |
||||||
|
if (url == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
new Ajax.Request(url, { |
||||||
|
onSuccess: function (response) { |
||||||
|
list.replace(response.responseText); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Reporting.onload(function () { |
||||||
|
if ($('query_saved_name') !== null) { |
||||||
|
if ($('query_saved_name').getAttribute("data-update-url") !== null) { |
||||||
|
Reporting.Controls.query_name_editor('query_saved_name'); |
||||||
|
} |
||||||
|
// don't concern ourselves with new queries
|
||||||
|
if ($('query_saved_name').getAttribute("data-is_new") !== null) { |
||||||
|
if ($('query-icon-delete') !== null) { |
||||||
|
Reporting.Controls.observe_click("query-icon-delete", Reporting.Controls.toggle_delete_form); |
||||||
|
Reporting.Controls.observe_click("query-icon-delete-cancel", Reporting.Controls.toggle_delete_form); |
||||||
|
$('delete_form').hide(); |
||||||
|
} |
||||||
|
|
||||||
|
if ($("query-breadcrumb-save") !== null) { |
||||||
|
// When saving an update of an exisiting query or apply filters, we replace the table on success
|
||||||
|
Reporting.Controls.attach_settings_callback($("query-breadcrumb-save"), Reporting.Controls.update_result_table); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Reporting.Controls.observe_click("query-icon-save-as", Reporting.Controls.toggle_save_as_form); |
||||||
|
Reporting.Controls.observe_click("query-icon-save-as-cancel", Reporting.Controls.toggle_save_as_form); |
||||||
|
if ($('save_as_form') !== null) { |
||||||
|
$('save_as_form').hide(); |
||||||
|
} |
||||||
|
|
||||||
|
// When saving a new query, the success-response is the new saved query's url -> redirect to that
|
||||||
|
Reporting.Controls.attach_settings_callback($("query-icon-save-button"), function (response) { |
||||||
|
Ajax.activeRequestCount = Ajax.activeRequestCount + 1; // HACK: Prevent Loading spinner from disappearing
|
||||||
|
document.location = response.responseText; |
||||||
|
}); |
||||||
|
// When saving an update of an exisiting query or apply filters, we replace the table on success
|
||||||
|
Reporting.Controls.attach_settings_callback($("query-icon-apply-button"), Reporting.Controls.update_result_table); |
||||||
|
Reporting.Controls.observe_click($('query-link-clear'), Reporting.Controls.clear_query); |
||||||
|
}); |
||||||
|
|
||||||
|
|
@ -0,0 +1,518 @@ |
|||||||
|
/*jslint white: false, nomen: true, devel: true, on: true, debug: false, evil: true, onevar: false, browser: true, white: false, indent: 2 */ |
||||||
|
/*global window, $, $$, Reporting, Effect, Ajax, Element, Form */ |
||||||
|
|
||||||
|
Reporting.Filters = { |
||||||
|
load_available_values_for_filter: function (filter_name, callback_func) { |
||||||
|
var select, radio_options, post_select_values; |
||||||
|
select = $$('.filter-value[data-filter-name="' + filter_name + '"]').first(); |
||||||
|
// check if we might have a radio-box
|
||||||
|
radio_options = $$('.' + filter_name + '_radio_option input'); |
||||||
|
if (radio_options && radio_options.size() !== 0) { |
||||||
|
radio_options.first().checked = true; |
||||||
|
callback_func(); |
||||||
|
} |
||||||
|
if (select === null || select === undefined) { |
||||||
|
return; |
||||||
|
} |
||||||
|
url = select.readAttribute("data-remote-url"); |
||||||
|
json_post_select_values = select.readAttribute('data-initially-selected') |
||||||
|
if (json_post_select_values !== null && json_post_select_values !== undefined) { |
||||||
|
post_select_values = json_post_select_values.replace(/'/g, '"').evalJSON(true); |
||||||
|
} |
||||||
|
if (select.readAttribute('data-loading') === "ajax" && select.childElements().length === 0) { |
||||||
|
if (window.global_prefix === undefined) { |
||||||
|
window.global_prefix = ""; |
||||||
|
} |
||||||
|
|
||||||
|
new Ajax.Updater({ success: select.id }, url, { |
||||||
|
parameters: { |
||||||
|
filter_name: filter_name, |
||||||
|
values: json_post_select_values |
||||||
|
}, |
||||||
|
insertion: 'bottom', |
||||||
|
evalScripts: false, |
||||||
|
onCreate: function (a, b) { |
||||||
|
$$("select[data-filter-name='" + filter_name + "']").each(function (e) { e.disable(); }); |
||||||
|
}, |
||||||
|
onComplete: function (a, b) { |
||||||
|
$$("select[data-filter-name='" + filter_name + "']").each(function (e) { e.enable(); }); |
||||||
|
if (select.tagName.toLowerCase() === "select") { |
||||||
|
if (post_select_values === undefined || post_select_values === null || post_select_values.size() === 0) { |
||||||
|
select.selectedIndex = 0; |
||||||
|
} else { |
||||||
|
Reporting.Filters.select_values(select, post_select_values); |
||||||
|
} |
||||||
|
} |
||||||
|
callback_func(); |
||||||
|
} |
||||||
|
}); |
||||||
|
Reporting.Filters.multi_select(select, false); |
||||||
|
} else { |
||||||
|
callback_func(); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
show_filter: function (field, options) { |
||||||
|
if (options === undefined) { |
||||||
|
options = {}; |
||||||
|
} |
||||||
|
if (options.callback_func === undefined) { |
||||||
|
options.callback_func = function () {}; |
||||||
|
} |
||||||
|
if (options.slowly === undefined) { |
||||||
|
options.slowly = false; |
||||||
|
} |
||||||
|
if (options.show_filter === undefined) { |
||||||
|
options.show_filter = true; |
||||||
|
} |
||||||
|
if (options.hide_only === undefined) { |
||||||
|
options.hide_only = false; |
||||||
|
} |
||||||
|
var field_el = $('tr_' + field); |
||||||
|
if (field_el !== null) { |
||||||
|
if (options.insert_after === undefined) { |
||||||
|
options.insert_after = Reporting.Filters.last_visible_filter(); |
||||||
|
} |
||||||
|
if (options.insert_after !== undefined && options.show_filter) { |
||||||
|
// Move the filter down to appear after the last currently visible filter
|
||||||
|
if (field_el.id !== options.insert_after.id) { |
||||||
|
field_el.remove(); |
||||||
|
options.insert_after.insert({after: field_el}); |
||||||
|
} |
||||||
|
} |
||||||
|
// the following command might be included into the callback_function (which is called after the ajax request) later
|
||||||
|
var display_functor; |
||||||
|
if (options.show_filter) { |
||||||
|
(options.slowly ? Effect.Appear : Element.show)(field_el); |
||||||
|
Reporting.Filters.load_available_values_for_filter(field, options.callback_func); |
||||||
|
$('rm_' + field).value = field; // set the value, so the serialized form will return this filter
|
||||||
|
Reporting.Filters.value_changed(field); |
||||||
|
Reporting.Filters.set_filter_value_widths(100); |
||||||
|
} else { |
||||||
|
(options.slowly ? Effect.Fade : Element.hide)(field_el); |
||||||
|
if (!options.hide_only) { // remember that this filter used to be selected
|
||||||
|
field_el.removeAttribute('data-selected'); |
||||||
|
} |
||||||
|
$('rm_' + field).value = ""; // reset the value, so the serialized form will not return this filter
|
||||||
|
Reporting.Filters.set_filter_value_widths(5000); |
||||||
|
} |
||||||
|
Reporting.Filters.operator_changed(field, $("operators[" + field + "]")); |
||||||
|
Reporting.Filters.display_category($(field_el.getAttribute("data-label"))); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* Activates the filter with the given name and loads dependent filters if necessary. |
||||||
|
* |
||||||
|
* @param filter_name Name of the filter to be activated. |
||||||
|
*/ |
||||||
|
add_filter: function (filter_name, activate_dependent, on_complete) { |
||||||
|
var field = filter_name |
||||||
|
if (activate_dependent === undefined) { |
||||||
|
activate_dependent = true; |
||||||
|
} |
||||||
|
if (on_complete === undefined) { |
||||||
|
on_complete = function() { }; |
||||||
|
} |
||||||
|
// do this immediately instead of in callback to avoid concurrency issues during testing
|
||||||
|
Reporting.Filters.select_option_enabled($("add_filter_select"), filter_name, false); |
||||||
|
Reporting.Filters.show_filter(field, { slowly: true, callback_func: function() { |
||||||
|
if (activate_dependent) { |
||||||
|
Reporting.Filters.activate_dependents($(field + "_arg_1_val")); |
||||||
|
} |
||||||
|
on_complete(); |
||||||
|
} |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
remove_filter: function (field, hide_only) { |
||||||
|
Reporting.Filters.show_filter(field, { show_filter: false, hide_only: hide_only }); |
||||||
|
var dependent = Reporting.Filters.get_dependents($(field + '_arg_1_val'), false).find(function(d) { |
||||||
|
return Reporting.Filters.visible_filters().include(d); |
||||||
|
}); |
||||||
|
if (dependent !== undefined) { |
||||||
|
Reporting.Filters.remove_filter(dependent); |
||||||
|
} |
||||||
|
Reporting.Filters.select_option_enabled($("add_filter_select"), field, true); |
||||||
|
}, |
||||||
|
|
||||||
|
/* |
||||||
|
Smoothly sets the width of currently displayed filters. |
||||||
|
Params: |
||||||
|
delay:Int |
||||||
|
Time to wait before resizing the filters width */ |
||||||
|
set_filter_value_widths: function (delay) { |
||||||
|
window.clearTimeout(Reporting.Filters.set_filter_value_widths_timeout); |
||||||
|
if (Reporting.Filters.visible_filters().size() > 0) { |
||||||
|
Reporting.Filters.set_filter_value_widths_timeout = window.setTimeout(function () { |
||||||
|
var table_data = $("tr_" + Reporting.Filters.visible_filters().first()).select(".filter_values").first().up(); |
||||||
|
var current_width = table_data.getWidth(); |
||||||
|
var filter_values = $($$(".filter_values")); |
||||||
|
// First, reset all widths
|
||||||
|
filter_values.each(function (f) { |
||||||
|
$(f).up().style.width = "auto"; |
||||||
|
}); |
||||||
|
// Now, get the current width
|
||||||
|
// Any width will be fine, as the table layout makes all elements the same width
|
||||||
|
var new_width = table_data.getWidth(); |
||||||
|
if (new_width < current_width) { |
||||||
|
// Set all widths to previous, so we can animate
|
||||||
|
filter_values.each(function (f) { |
||||||
|
$(f).up().style.width = current_width + "px"; |
||||||
|
}); |
||||||
|
} |
||||||
|
// Now, set all widths to be the widest
|
||||||
|
filter_values.each(function (f) { |
||||||
|
if (new_width < current_width) { |
||||||
|
$(f).up().morph("width: " + new_width + "px;"); |
||||||
|
} else { |
||||||
|
$(f).up().style.width = new_width + "px"; |
||||||
|
} |
||||||
|
}); |
||||||
|
}, delay); |
||||||
|
} |
||||||
|
}, |
||||||
|
set_filter_value_widths_timeout: undefined, |
||||||
|
|
||||||
|
last_visible_filter: function () { |
||||||
|
return $($$('.filter')).reverse().detect(function (f) { |
||||||
|
return f.visible(); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
/* Display the given category if any of its filters are visible. Otherwise hide it */ |
||||||
|
display_category: function (label) { |
||||||
|
if (label !== null) { |
||||||
|
var filters = $$('.filter'); |
||||||
|
for (var i = 0; i < filters.length; i += 1) { |
||||||
|
if (filters[i].visible() && filters[i].getAttribute("data-label") === label) { |
||||||
|
Element.show(label); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
Element.hide(label); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
operator_changed: function (field, select) { |
||||||
|
var option_tag, arity, first; |
||||||
|
if (select === null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
first = false |
||||||
|
if (select.getAttribute("data-first") === undefined || select.getAttribute("data-first") === null) { |
||||||
|
first = true; |
||||||
|
$(select).setAttribute("data-first", "false"); |
||||||
|
} |
||||||
|
option_tag = select.options[select.selectedIndex]; |
||||||
|
arity = parseInt(option_tag.getAttribute("data-arity"), 10); |
||||||
|
Reporting.Filters.change_argument_visibility(field, arity); |
||||||
|
if (option_tag.getAttribute("data-forced") !== undefined && option_tag.getAttribute("data-forced") !== null) { |
||||||
|
Reporting.Filters.force_type(option_tag, first); |
||||||
|
}; |
||||||
|
}, |
||||||
|
|
||||||
|
// Overwrite to customize input enforcements.
|
||||||
|
// option: 'option' HTMLElement
|
||||||
|
// first: Boolean indicating whether the operator changed for the first time
|
||||||
|
force_type: function (option, first) { |
||||||
|
true; |
||||||
|
}, |
||||||
|
|
||||||
|
value_changed: function (field) { |
||||||
|
var val, tr; |
||||||
|
val = $(field + '_arg_1_val'); |
||||||
|
tr = $('tr_' + field); |
||||||
|
if (!val) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (val.value === '<<inactive>>') { |
||||||
|
tr.addClassName('inactive-filter'); |
||||||
|
} else { |
||||||
|
tr.removeClassName('inactive-filter'); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
change_argument_visibility: function (field, arg_nr) { |
||||||
|
var params, i; |
||||||
|
params = [$(field + '_arg_1'), $(field + '_arg_2')]; |
||||||
|
|
||||||
|
for (i = 0; i < 2; i += 1) { |
||||||
|
if (params[i] !== null) { |
||||||
|
if (arg_nr >= (i + 1) || arg_nr <= (-1 - i)) { |
||||||
|
params[i].show(); |
||||||
|
params[i].descendants().each(function (desc) { desc.show(); }); |
||||||
|
} else { |
||||||
|
params[i].hide(); |
||||||
|
params[i].descendants().each(function (desc) { desc.hide(); }); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
select_option_enabled: function (box, value, state) { |
||||||
|
var option = box.select("[value='" + value + "']").first(); |
||||||
|
if (option !== undefined) { |
||||||
|
option.disabled = !state; |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
multi_select: function (select, multi) { |
||||||
|
select.multiple = multi; |
||||||
|
if (multi) { |
||||||
|
select.size = 4; |
||||||
|
// deselect first option if it's present
|
||||||
|
if (select.options[0] !== undefined && select.options[0] !== null) { |
||||||
|
select.options[0].selected = false; |
||||||
|
} |
||||||
|
} else { |
||||||
|
select.size = 1; |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
toggle_multi_select: function (select) { |
||||||
|
Reporting.Filters.multi_select(select, !select.multiple); |
||||||
|
}, |
||||||
|
|
||||||
|
visible_filters: function () { |
||||||
|
return $("filter_table").select("tr").select(function (tr) { |
||||||
|
return tr.visible() === true; |
||||||
|
}).collect(function (filter) { |
||||||
|
return filter.getAttribute("data-filter-name"); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
clear: function () { |
||||||
|
Reporting.Filters.visible_filters().each(function (filter) { |
||||||
|
Reporting.Filters.remove_filter(filter); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
// Returns an array of dependents of the given element
|
||||||
|
// get_all -> Boolean: whether to return all dependends (even the
|
||||||
|
// dependents of this filters dependents) or not
|
||||||
|
get_dependents: function (element, get_all) { |
||||||
|
var dependent_field = "data-all-dependents"; |
||||||
|
if (get_all === false) { |
||||||
|
dependent_field = "data-next-dependents"; |
||||||
|
} |
||||||
|
if (element.hasAttribute(dependent_field)) { |
||||||
|
return element.getAttribute(dependent_field).replace(/'/g, '"').evalJSON(true); |
||||||
|
} else { |
||||||
|
return []; |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// Activate the first dependent of the changed filter, if it is not already active.
|
||||||
|
// Afterwards, collect the visible filters from the dependents list and start
|
||||||
|
// narrowing down their values.
|
||||||
|
// Param: select [optional] - the select-box of the filter which should activate it's dependents
|
||||||
|
activate_dependents: function (selectBox, callbackWhenFinished) { |
||||||
|
var all_dependents, next_dependents, dependent, active_filters, source; |
||||||
|
if (selectBox === undefined || (selectBox.type && selectBox.type.toLowerCase() == 'change')) { |
||||||
|
selectBox = this; |
||||||
|
} |
||||||
|
if (selectBox.tagName.toLowerCase() !== "select") { |
||||||
|
return; // only multi_value filters have dependents
|
||||||
|
} |
||||||
|
if (callbackWhenFinished === undefined) { |
||||||
|
callbackWhenFinished = function(dependent) { }; |
||||||
|
} |
||||||
|
source = selectBox.getAttribute("data-filter-name"); |
||||||
|
all_dependents = Reporting.Filters.get_dependents(selectBox); |
||||||
|
next_dependents = Reporting.Filters.get_dependents(selectBox, false); |
||||||
|
dependent = Reporting.Filters.which_dependent_shall_i_take(source, next_dependents); |
||||||
|
if (!dependent) { |
||||||
|
return; |
||||||
|
} |
||||||
|
active_filters = Reporting.Filters.visible_filters(); |
||||||
|
|
||||||
|
if (!active_filters.include(dependent)) { |
||||||
|
// in case we run into a situation where the dependent to show is not in the currently selected dependency chain
|
||||||
|
// we have to remove all filters until we reach the source and add the new dependent
|
||||||
|
if (next_dependents.any( function(d){ return active_filters.include(d) } )) { |
||||||
|
while (active_filters.last() !== source) { |
||||||
|
Reporting.Filters.show_filter(active_filters.pop(1), { show_filter: false, slowly: true }); |
||||||
|
} |
||||||
|
} |
||||||
|
Reporting.Filters.show_filter(dependent, { slowly: true, insert_after: $(selectBox.up(".filter")) }); |
||||||
|
// render filter inactive if possible to avoid unintended filtering
|
||||||
|
$(dependent + '_arg_1_val').value = '<<inactive>>' |
||||||
|
Reporting.Filters.operator_changed(dependent, $('operators[' + dependent + ']')); |
||||||
|
// Hide remove box of dependent
|
||||||
|
$('rm_box_' + dependent).hide(); |
||||||
|
// Remove border of dependent, so it "merges" with the filter before
|
||||||
|
$('tr_' + dependent).addClassName("no-border"); |
||||||
|
active_filters.unshift(dependent); |
||||||
|
} |
||||||
|
setTimeout(function () { // Make sure the newly shown filters are in the DOM
|
||||||
|
var active_dependents = all_dependents.select(function (d) { |
||||||
|
return active_filters.include(d); |
||||||
|
}); |
||||||
|
Reporting.Filters.narrow_values( |
||||||
|
Reporting.Filters.dependent_for(source), |
||||||
|
active_dependents, |
||||||
|
function() { callbackWhenFinished(dependent); } |
||||||
|
); |
||||||
|
}, 1); |
||||||
|
}, |
||||||
|
|
||||||
|
// return an array of all filters that depend on the given filter plus the given filter
|
||||||
|
dependent_for: function(field) { |
||||||
|
var deps = $$('.filters-select[data-all-dependents]').findAll(function(selectBox) { |
||||||
|
return (selectBox.up('tr').visible()) && Reporting.Filters.get_dependents(selectBox).include(field) |
||||||
|
}).map(function(selectBox) { |
||||||
|
return selectBox.getAttribute("data-filter-name"); |
||||||
|
}); |
||||||
|
return deps === undefined ? [ field ] : [ field ].concat(deps) |
||||||
|
}, |
||||||
|
|
||||||
|
// Select the given values of the selectBox.
|
||||||
|
// Toggle multi-select state of the selectBox depending on how many values were given.
|
||||||
|
select_values: function(selectBox, values_to_select) { |
||||||
|
Reporting.Filters.multi_select(selectBox, values_to_select.size() > 1); |
||||||
|
values_to_select.each(function (val) { |
||||||
|
var opt = selectBox.select("option[value='" + val + "']"); |
||||||
|
if (opt.size() === 1) { |
||||||
|
opt.first().selected = true; |
||||||
|
} |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
exists: function (filter) { |
||||||
|
return Reporting.Filters.visible_filters().include(filter); |
||||||
|
}, |
||||||
|
|
||||||
|
// Narrow down the available values for the [dependents] of [sources].
|
||||||
|
// This will narrow down for each dependent separately, adding each finished
|
||||||
|
// dependent to the sources array and removing it from the dependents array.
|
||||||
|
narrow_values: function (sources, dependents, callbackWhenFinished) { |
||||||
|
if (sources.size() === 0 || dependents.size === 0 || dependents.first() === undefined) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (callbackWhenFinished === undefined) { |
||||||
|
callbackWhenFinished = function() {}; |
||||||
|
} |
||||||
|
var params = document.location.href.include('?') ? '&' : '?' |
||||||
|
params = params + "narrow_values=1&dependent=" + dependents.first(); |
||||||
|
sources.each(function (filter) { |
||||||
|
params = params + "&sources[]=" + filter; |
||||||
|
}); |
||||||
|
var targetUrl = document.location.href + params; |
||||||
|
var currentDependent = dependents.first(); |
||||||
|
var updater = new Ajax.Request(targetUrl, |
||||||
|
{ |
||||||
|
asynchronous: true, |
||||||
|
evalScripts: true, |
||||||
|
postBody: Reporting.Controls.serialize_settings_form(), |
||||||
|
onSuccess: function (response) { |
||||||
|
Reporting.clearFlash(); |
||||||
|
if (response.responseJSON !== undefined) { |
||||||
|
var continue_narrowing = true; |
||||||
|
var selectBox = $(currentDependent + "_arg_1_val"); |
||||||
|
var selected = selectBox.select("option").collect(function (sel) { |
||||||
|
if (sel.selected) { |
||||||
|
return sel.value; |
||||||
|
} |
||||||
|
}).compact(); |
||||||
|
// remove old values
|
||||||
|
$(selectBox).childElements().each(function (o) { |
||||||
|
o.remove(); |
||||||
|
}); |
||||||
|
// insert new values
|
||||||
|
response.responseJSON.each(function (o) { |
||||||
|
var ary = [ (o === null ? "" : o) ].flatten(); |
||||||
|
var label = ary.first(); |
||||||
|
var value = ary.last(); |
||||||
|
// cannot use .innerhtml due to IE wierdness
|
||||||
|
$(selectBox).insert(new Element('option', {value: value}).update(label.escapeHTML())); |
||||||
|
}); |
||||||
|
|
||||||
|
Reporting.Filters.select_values(selectBox, selected); |
||||||
|
|
||||||
|
sources.push(currentDependent); // Add as last element
|
||||||
|
dependents.splice(0, 1); // Delete first element
|
||||||
|
// if we got no values besides the <<inactive>> value, do not show this selectBox
|
||||||
|
if (!selectBox.select("option").any(function (opt) { return opt.value != '<<inactive>>' })) { |
||||||
|
Reporting.Filters.show_filter(currentDependent, { show_filter: false }); |
||||||
|
continue_narrowing = false; |
||||||
|
} |
||||||
|
// if the current filter is inactive, hide dependent - otherwise recurisvely narrow dependent values
|
||||||
|
if (selectBox.value == '<<inactive>>') { |
||||||
|
Reporting.Filters.value_changed(currentDependent); |
||||||
|
dependents.each(function (dependent) { |
||||||
|
Reporting.Filters.show_filter(dependent, { |
||||||
|
slowly: true, |
||||||
|
show_filter: false }); |
||||||
|
}); |
||||||
|
continue_narrowing = false; |
||||||
|
} |
||||||
|
if (continue_narrowing) { |
||||||
|
Reporting.Filters.narrow_values(sources, dependents); |
||||||
|
} |
||||||
|
callbackWhenFinished(); |
||||||
|
} |
||||||
|
}, |
||||||
|
onException: function (response, error) { |
||||||
|
if (console) { |
||||||
|
console.log(error); |
||||||
|
} |
||||||
|
Reporting.flash("Loading of filter values failed. Probably, the server is temporary offline for maintenance."); |
||||||
|
var selectBox = $(currentDependent + "_arg_1_val"); |
||||||
|
$(selectBox).insert(new Element('option', {value: '<<inactive>>'}).update('Failed to load values.')); |
||||||
|
} |
||||||
|
} |
||||||
|
); |
||||||
|
}, |
||||||
|
|
||||||
|
// This method may be overridden by the actual application to define custon behavior
|
||||||
|
// If there are multiple possible dependents to follow.
|
||||||
|
// The dependent to follow should be returned.
|
||||||
|
which_dependent_shall_i_take: function(source, dependents) { |
||||||
|
return dependents.first(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Reporting.onload(function () { |
||||||
|
if ($("add_filter_select")) { |
||||||
|
$("add_filter_select").observe("change", function () { |
||||||
|
if (!(Reporting.Filters.exists(this.value))) { |
||||||
|
Reporting.Filters.add_filter(this.value); |
||||||
|
this.selectedIndex = 0; |
||||||
|
}; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
$$(".filter_rem").each(function (e) { |
||||||
|
e.observe("click", function () { |
||||||
|
var filter_name = this.up('tr').getAttribute("data-filter-name"); |
||||||
|
Reporting.Filters.remove_filter(filter_name); |
||||||
|
}); |
||||||
|
}); |
||||||
|
$$(".filter_operator").each(function (e) { |
||||||
|
e.observe("change", function (evt) { |
||||||
|
var filter_name = this.getAttribute("data-filter-name"); |
||||||
|
Reporting.Filters.operator_changed(filter_name, this); |
||||||
|
Reporting.fireEvent($(filter_name + "_arg_1_val"), "change"); |
||||||
|
}); |
||||||
|
}); |
||||||
|
$$(".filter_multi-select").each(function (e) { |
||||||
|
e.observe("click", function () { |
||||||
|
Reporting.Filters.toggle_multi_select($(this.getAttribute("data-filter-name") + '_arg_1_val')); |
||||||
|
}); |
||||||
|
}); |
||||||
|
$$(".filters-select").each(function (s) { |
||||||
|
var selected_size = Array.from(s.options).findAll(function (o) { |
||||||
|
return o.selected === true; |
||||||
|
}).size(); |
||||||
|
s.multiple = (selected_size > 1); |
||||||
|
s.observe("change", function (evt) { |
||||||
|
var filter_name = this.up('tr').getAttribute("data-filter-name"); |
||||||
|
Reporting.Filters.value_changed(filter_name); |
||||||
|
}); |
||||||
|
}); |
||||||
|
$$('.filters-select[data-all-dependents]').each(function (dependency) { |
||||||
|
dependency.observe("change", Reporting.Filters.activate_dependents); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
@ -0,0 +1,208 @@ |
|||||||
|
/*jslint white: false, nomen: true, devel: true, on: true, debug: false, evil: true, onevar: false, browser: true, white: false, indent: 2 */ |
||||||
|
/*global window, $, $$, Reporting, Effect, Ajax, selectAllOptions, moveOptions, moveOptionUp, moveOptionDown */ |
||||||
|
|
||||||
|
Reporting.GroupBys = { |
||||||
|
group_by_container_ids: function() { |
||||||
|
var ids = $w('group_by_columns group_by_rows'); |
||||||
|
return ids.select(function (i) { |
||||||
|
return $(i) !== null |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
sortable_options: function() { |
||||||
|
return { |
||||||
|
tag: 'span', |
||||||
|
only: "drag_element", |
||||||
|
overlap: 'horizontal', |
||||||
|
constraint:'horizontal', |
||||||
|
containment: Reporting.GroupBys.group_by_container_ids(), |
||||||
|
dropOnEmpty: true, |
||||||
|
hoverclass: 'drag_container_accept' |
||||||
|
}; |
||||||
|
}, |
||||||
|
|
||||||
|
recreate_sortables: function() { |
||||||
|
Reporting.GroupBys.group_by_container_ids().each(function(id) { |
||||||
|
Sortable.create(id, Reporting.GroupBys.sortable_options()); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
initialize_drag_and_drop_areas: function() { |
||||||
|
Reporting.GroupBys.recreate_sortables(); |
||||||
|
}, |
||||||
|
|
||||||
|
create_label: function(group_by, text) { |
||||||
|
return new Element('label', { |
||||||
|
'class': 'in_row group_by_label', |
||||||
|
'for': group_by.identify(), |
||||||
|
'id': group_by.identify() + '_label' |
||||||
|
}).update(text); |
||||||
|
}, |
||||||
|
|
||||||
|
create_remove_button: function(group_by) { |
||||||
|
var remove_link, remove_icon; |
||||||
|
remove_link = new Element('a', { |
||||||
|
'class': 'group_by_remove in_row', |
||||||
|
'id': group_by.identify() + '_remove', |
||||||
|
'href': 'javascript:' |
||||||
|
}); |
||||||
|
remove_icon = $('hidden_remove_img').clone(); |
||||||
|
remove_icon.removeAttribute('id'); |
||||||
|
remove_icon.removeAttribute('style'); |
||||||
|
|
||||||
|
if (Reporting._LA != undefined) { |
||||||
|
remove_link.setAttribute('title', Reporting._LA["REMOVE"] + ' ' + group_by.down('label').innerHTML); |
||||||
|
remove_icon.setAttribute('alt', Reporting._LA["REMOVE"] + ' ' + group_by.down('label').innerHTML); |
||||||
|
} |
||||||
|
remove_link.observe('click', function(e) { |
||||||
|
Reporting.GroupBys.remove_element_event_action(e, group_by, remove_link) |
||||||
|
}); |
||||||
|
remove_link.observe('keypress', function(e) { |
||||||
|
/* keyCode 32: Space */ |
||||||
|
if (e.keyCode == 32) { |
||||||
|
e.preventDefault(); |
||||||
|
Reporting.GroupBys.remove_element_event_action(e, group_by, remove_link) |
||||||
|
} |
||||||
|
}); |
||||||
|
remove_link.appendChild(remove_icon); |
||||||
|
return remove_link; |
||||||
|
}, |
||||||
|
|
||||||
|
remove_element_event_action: function(event, group_by, button) { |
||||||
|
var node; |
||||||
|
if (node = group_by.next('span')) { |
||||||
|
node = node.down('a'); |
||||||
|
if (node) { |
||||||
|
node.focus(); |
||||||
|
} |
||||||
|
} |
||||||
|
else if (node = group_by.next('select')) { |
||||||
|
node.focus(); |
||||||
|
} |
||||||
|
Reporting.GroupBys.remove_group_by(button.up('.group_by_element')); |
||||||
|
}, |
||||||
|
|
||||||
|
create_arrow: function(group_by, position) { |
||||||
|
return new Element('span', { |
||||||
|
'class': 'arrow in_row arrow_' + position, |
||||||
|
'id': group_by.identify() + '_arrow_' + position |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
create_group_by: function(field, caption) { |
||||||
|
var group_by, label, right_arrow, left_arrow, remove_button; |
||||||
|
group_by = new Element('span', { |
||||||
|
'class': 'in_row drag_element group_by_element', |
||||||
|
'data-group-by': field |
||||||
|
}); |
||||||
|
group_by.identify(); // give it a unique id
|
||||||
|
|
||||||
|
left_arrow = Reporting.GroupBys.create_arrow(group_by, 'left'); |
||||||
|
group_by.appendChild(left_arrow); |
||||||
|
|
||||||
|
label = Reporting.GroupBys.create_label(group_by, caption); |
||||||
|
Reporting.GroupBys.init_group_by_hover_effects([group_by, label]); |
||||||
|
group_by.appendChild(label); |
||||||
|
|
||||||
|
remove_button = Reporting.GroupBys.create_remove_button(group_by); |
||||||
|
group_by.appendChild(remove_button); |
||||||
|
|
||||||
|
right_arrow = Reporting.GroupBys.create_arrow(group_by, 'right'); |
||||||
|
group_by.appendChild(right_arrow); |
||||||
|
return group_by; |
||||||
|
}, |
||||||
|
|
||||||
|
// on mouse_over of a group_by or it's label, change the color of the group_by
|
||||||
|
// also change the color of the arrows
|
||||||
|
init_group_by_hover_effects: function(elements) { |
||||||
|
elements.each(function(element) { |
||||||
|
['mouseover', 'mouseout'].each(function(event_type) { |
||||||
|
element.observe(event_type, function(event) { |
||||||
|
Reporting.GroupBys.group_by_hover_effect(event, event_type == 'mouseover'); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
group_by_hover_effect: function(event, do_hover) { |
||||||
|
var group_by = $(Event.element(event)); |
||||||
|
// we possibly hit a tag inside the group_by, so go search the group_by then
|
||||||
|
if (!group_by.hasClassName('group_by_element')) { |
||||||
|
group_by = group_by.up('.group_by_element'); |
||||||
|
} |
||||||
|
if (group_by !== null) { |
||||||
|
Reporting.GroupBys.group_by_hover(group_by, do_hover); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
group_by_hover: function(group_by, state) { |
||||||
|
if (state) { |
||||||
|
group_by.childElements().each(function(e) { e.addClassName('hover'); }); |
||||||
|
} else { |
||||||
|
group_by.childElements().each(function(e) { e.removeClassName('hover'); }); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// This is whether it is possible to add a new group if <<field>> through the
|
||||||
|
// add-group-by select-box or not.
|
||||||
|
adding_group_by_enabled: function(field, state) { |
||||||
|
$w('add_group_by_columns add_group_by_rows').each(function(container_id) { |
||||||
|
Reporting.Filters.select_option_enabled($(container_id), field, state); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
remove_group_by: function(group_by) { |
||||||
|
Reporting.GroupBys.adding_group_by_enabled(group_by.readAttribute('data-group-by'), true); |
||||||
|
group_by.remove(); |
||||||
|
}, |
||||||
|
|
||||||
|
add_group_by_from_select: function(select) { |
||||||
|
var field, caption, container, selected_option; |
||||||
|
field = $(select).getValue(); |
||||||
|
container = select.up('.drag_container'); |
||||||
|
selected_option = select.select("[value='" + field + "']").first(); |
||||||
|
caption = selected_option.readAttribute('data-label'); |
||||||
|
Reporting.GroupBys.add_group_by(field, caption, container); |
||||||
|
select.select("[value='']").first().selected = true; |
||||||
|
}, |
||||||
|
|
||||||
|
add_group_by: function(field, caption, container) { |
||||||
|
var group_by, add_groups_select_box; |
||||||
|
add_groups_select_box = container.select('select').first(); |
||||||
|
group_by = Reporting.GroupBys.create_group_by(field, caption); |
||||||
|
add_groups_select_box.insert({ before: group_by }); |
||||||
|
Reporting.GroupBys.adding_group_by_enabled(field, false); |
||||||
|
Reporting.GroupBys.recreate_sortables(); |
||||||
|
}, |
||||||
|
|
||||||
|
clear: function() { |
||||||
|
Reporting.GroupBys.visible_group_bys().each(function (group_by) { |
||||||
|
Reporting.GroupBys.remove_group_by(group_by); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
visible_group_bys: function() { |
||||||
|
return Reporting.GroupBys.group_by_container_ids().collect(function (container) { |
||||||
|
return $(container).select('[data-group-by]') |
||||||
|
}).flatten(); |
||||||
|
}, |
||||||
|
|
||||||
|
exists: function(group_by_name) { |
||||||
|
return Reporting.GroupBys.visible_group_bys().any(function (grp) { |
||||||
|
return grp.getAttribute('data-group-by') == group_by_name; |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Reporting.onload(function () { |
||||||
|
Reporting.GroupBys.initialize_drag_and_drop_areas(); |
||||||
|
[$('add_group_by_rows'), $('add_group_by_columns')].each(function (select) { |
||||||
|
if (select !== null) { |
||||||
|
select.observe("change", function () { |
||||||
|
if (!(Reporting.GroupBys.exists(this.value))) { |
||||||
|
Reporting.GroupBys.add_group_by_from_select(this); |
||||||
|
}; |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,3 @@ |
|||||||
|
Reporting._LA = {}; |
||||||
|
Reporting._LA["REMOVE"] = "Lösche"; |
||||||
|
Reporting._LA["RESPONSE_ERROR"] = "Bei der Bearbeitung der Anfrage ist ein Fehler aufgetreten."; |
@ -0,0 +1,3 @@ |
|||||||
|
Reporting._LA = {}; |
||||||
|
Reporting._LA["REMOVE"] = "Delete"; |
||||||
|
Reporting._LA["RESPONSE_ERROR"] = "There was an error handling the query."; |
@ -0,0 +1,49 @@ |
|||||||
|
/*jslint white: false, nomen: true, devel: true, on: true, debug: false, evil: true, onevar: false, browser: true, white: false, indent: 2 */ |
||||||
|
/*global window, $, $$, Reporting, Effect, Ajax */ |
||||||
|
|
||||||
|
Reporting.Progress = { |
||||||
|
|
||||||
|
abort: function () { |
||||||
|
if (window.progressbar !== undefined && window.progressbar !== null) { |
||||||
|
window.progressbar.stop(); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
replace_with_bar: function (element) { |
||||||
|
var parent = element.up(); |
||||||
|
var size = parseInt(element.getAttribute('data-query-size'), 10) || 500; |
||||||
|
element.remove(); |
||||||
|
window.progressbar = Reporting.Progress.add_bar_to_parent(parent); |
||||||
|
// Speed determined through laborous experimentation!
|
||||||
|
window.progressbar.options.interval = (size * (Math.log(size))) / 100000; |
||||||
|
window.progressbar.start(); |
||||||
|
}, |
||||||
|
|
||||||
|
add_bar_to_parent: function (parent) { |
||||||
|
parent.appendChild(new Element('div', { |
||||||
|
'id': 'progressbar_container', |
||||||
|
'class': 'progressbar_container' |
||||||
|
})); |
||||||
|
return new Control.ProgressBar('progressbar_container'); |
||||||
|
}, |
||||||
|
|
||||||
|
confirm_question: function () { |
||||||
|
var bar = $('progressbar'); |
||||||
|
if (bar !== null && bar !== undefined) { |
||||||
|
var size = bar.getAttribute('data-size'); |
||||||
|
var question = bar.getAttribute('data-translation'); |
||||||
|
if (confirm(question)) { |
||||||
|
var target = bar.getAttribute("data-target"); |
||||||
|
bar.up().show(); |
||||||
|
Reporting.Progress.replace_with_bar(bar); |
||||||
|
Reporting.Controls.send_settings_data(target, Reporting.Controls.update_result_table); |
||||||
|
} else { |
||||||
|
bar.toggle(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Reporting.onload(function () { |
||||||
|
Reporting.Progress.confirm_question(); |
||||||
|
}); |
@ -0,0 +1,116 @@ |
|||||||
|
/** |
||||||
|
* @author Ryan Johnson <http://syntacticx.com/>
|
||||||
|
* @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
|
||||||
|
* @package LivePipe UI |
||||||
|
* @license MIT |
||||||
|
* @url http://livepipe.net/control/progressbar
|
||||||
|
* @require prototype.js, livepipe.js |
||||||
|
*/ |
||||||
|
|
||||||
|
/*global document, Prototype, Ajax, Class, PeriodicalExecuter, $, $A, Control */ |
||||||
|
|
||||||
|
if (typeof(Prototype) === "undefined") { |
||||||
|
throw "Control.ProgressBar requires Prototype to be loaded."; |
||||||
|
} |
||||||
|
if (typeof(Event) === "undefined") { |
||||||
|
throw "Control.ProgressBar requires Event to be loaded."; |
||||||
|
} |
||||||
|
if (typeof(Control) === "undefined") { |
||||||
|
if (window.console && window.console.log) { |
||||||
|
window.console.log("Control.ProgressBar requires Prototype ~= 1.7"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
|
||||||
|
Control.ProgressBar = Class.create({ |
||||||
|
initialize: function(container, options) { |
||||||
|
this.progress = 0; |
||||||
|
this.executer = false; |
||||||
|
this.active = false; |
||||||
|
this.poller = false; |
||||||
|
this.container = $(container); |
||||||
|
this.containerWidth = this.container.getDimensions().width; |
||||||
|
this.progressContainer = $(document.createElement('div')); |
||||||
|
this.progressContainer.setStyle({ |
||||||
|
width: this.containerWidth + 'px', |
||||||
|
height: '100%', |
||||||
|
position: 'absolute', |
||||||
|
top: '0px', |
||||||
|
right: '0px' |
||||||
|
}); |
||||||
|
this.container.appendChild(this.progressContainer); |
||||||
|
this.options = { |
||||||
|
afterChange: Prototype.emptyFunction, |
||||||
|
interval: 0.25, |
||||||
|
step: 1, |
||||||
|
classNames: { |
||||||
|
active: 'progress_bar_active', |
||||||
|
inactive: 'progress_bar_inactive' |
||||||
|
} |
||||||
|
}; |
||||||
|
Object.extend(this.options, options || {}); |
||||||
|
this.container.addClassName(this.options.classNames.inactive); |
||||||
|
this.active = false; |
||||||
|
}, |
||||||
|
setProgress: function (value) { |
||||||
|
this.progress = value; |
||||||
|
this.draw(); |
||||||
|
if (this.progress >= 100) { |
||||||
|
this.stop(false); |
||||||
|
} |
||||||
|
this.notify('afterChange', this.progress, this.active); |
||||||
|
}, |
||||||
|
poll: function (url, interval, ajaxOptions) { |
||||||
|
// Extend the passed ajax options and success callback with our own.
|
||||||
|
ajaxOptions = ajaxOptions || {}; |
||||||
|
var success = ajaxOptions.onSuccess || Prototype.emptyFunction; |
||||||
|
ajaxOptions.onSuccess = success.wrap(function (callOriginal, request) { |
||||||
|
this.setProgress(parseInt(request.responseText, 10)); |
||||||
|
if (!this.active) { |
||||||
|
this.poller.stop(); |
||||||
|
} |
||||||
|
callOriginal(request); |
||||||
|
}).bind(this); |
||||||
|
|
||||||
|
this.active = true; |
||||||
|
this.poller = new PeriodicalExecuter(function () { |
||||||
|
var a = new Ajax.Request(url, ajaxOptions); |
||||||
|
}.bindAsEventListener(this), interval || 3); |
||||||
|
}, |
||||||
|
start: function () { |
||||||
|
this.active = true; |
||||||
|
this.container.removeClassName(this.options.classNames.inactive); |
||||||
|
this.container.addClassName(this.options.classNames.active); |
||||||
|
this.executer = new PeriodicalExecuter(this.step.bind(this, this.options.step), this.options.interval); |
||||||
|
}, |
||||||
|
stop: function (reset) { |
||||||
|
this.active = false; |
||||||
|
if (this.executer) { |
||||||
|
this.executer.stop(); |
||||||
|
} |
||||||
|
this.container.removeClassName(this.options.classNames.active); |
||||||
|
this.container.addClassName(this.options.classNames.inactive); |
||||||
|
if (typeof reset === 'undefined' || reset === true) { |
||||||
|
this.reset(); |
||||||
|
} |
||||||
|
}, |
||||||
|
step: function (amount) { |
||||||
|
this.active = true; |
||||||
|
this.setProgress(Math.min(100, this.progress + amount)); |
||||||
|
}, |
||||||
|
reset: function () { |
||||||
|
this.active = false; |
||||||
|
this.setProgress(0); |
||||||
|
}, |
||||||
|
draw: function () { |
||||||
|
this.progressContainer.setStyle({ |
||||||
|
width: (100 - this.progress) + "%" |
||||||
|
}); |
||||||
|
}, |
||||||
|
notify: function (event_name) { |
||||||
|
if (this.options[event_name]) { |
||||||
|
return [this.options[event_name].apply(this.options[event_name], $A(arguments).slice(1))]; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
Event.extend(Control.ProgressBar); |
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
/*jslint white: false, nomen: true, devel: true, on: true, debug: false, evil: true, onevar: false, browser: true, white: false, indent: 2 */ |
||||||
|
/*global window, $, $$, Reporting, Effect, Ajax */ |
||||||
|
|
||||||
|
Reporting.RestoreQuery = { |
||||||
|
|
||||||
|
select_operator: function (field, operator) { |
||||||
|
var select, i; |
||||||
|
select = $("operators_" + field); |
||||||
|
if (select === null) { |
||||||
|
return; // there is no such operator select field
|
||||||
|
} |
||||||
|
for (i = 0; i < select.options.length; i += 1) { |
||||||
|
if (select.options[i].value === operator) { |
||||||
|
select.selectedIndex = i; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
Reporting.Filters.operator_changed(field, select); |
||||||
|
}, |
||||||
|
|
||||||
|
disable_select_option: function (select, field) { |
||||||
|
for (var i = 0; i < select.options.length; i += 1) { |
||||||
|
if (select.options[i].value === field) { |
||||||
|
select.options[i].disabled = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
restore_dependent_filters: function(filter_name) { |
||||||
|
$$("tr.filter[data-filter-name=" + filter_name + "] select.filter-value").each(function(selectBox) { |
||||||
|
var activateNext = function(dependent) { |
||||||
|
if (!dependent) return; |
||||||
|
Reporting.RestoreQuery.restore_dependent_filters(dependent); |
||||||
|
}; |
||||||
|
if (selectBox.hasAttribute('data-initially-selected')) { |
||||||
|
var selected_values = selectBox.readAttribute('data-initially-selected').replace(/'/g, '"').evalJSON(true); |
||||||
|
Reporting.Filters.select_values(selectBox, selected_values); |
||||||
|
Reporting.Filters.value_changed(filter_name); |
||||||
|
} |
||||||
|
if (selectBox.getValue() !== '<<inactive>>') { |
||||||
|
Reporting.Filters.activate_dependents(selectBox, activateNext); |
||||||
|
} |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
restore_filters: function () { |
||||||
|
var deps = $$('.filters-select.filter-value').each(function(select) { |
||||||
|
var tr = select.up('tr'); |
||||||
|
if (tr.visible()) { |
||||||
|
var filter = tr.readAttribute('data-filter-name'); |
||||||
|
var dependent = select.readAttribute('data-dependent'); |
||||||
|
if (filter && dependent) { |
||||||
|
Reporting.Filters.remove_filter(filter, false); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
$$("tr[data-selected=true]").each(function (e) { |
||||||
|
var select = e.down(".filter_values select"); |
||||||
|
if (select && select.hasAttribute("data-dependent")) return; |
||||||
|
var filter_name = e.getAttribute("data-filter-name"); |
||||||
|
var on_complete = function() { |
||||||
|
Reporting.RestoreQuery.restore_dependent_filters(filter_name); |
||||||
|
}; |
||||||
|
Reporting.Filters.add_filter(filter_name, false, on_complete); |
||||||
|
}); |
||||||
|
}, |
||||||
|
|
||||||
|
restore_group_bys: function () { |
||||||
|
Reporting.GroupBys.group_by_container_ids().each(function(id) { |
||||||
|
var container, selected_groups; |
||||||
|
container = $(id); |
||||||
|
if (container.hasAttribute('data-initially-selected')) { |
||||||
|
selected_groups = container.readAttribute('data-initially-selected').replace(/'/g, '"').evalJSON(true); |
||||||
|
selected_groups.each(function(group_and_label) { |
||||||
|
var group, label; |
||||||
|
group = group_and_label[0]; |
||||||
|
label = group_and_label[1]; |
||||||
|
Reporting.GroupBys.add_group_by(group, label, container); |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Reporting.onload(function () { |
||||||
|
Reporting.RestoreQuery.restore_group_bys(); |
||||||
|
Reporting.RestoreQuery.restore_filters(); |
||||||
|
}); |
@ -0,0 +1,306 @@ |
|||||||
|
/* |
||||||
|
Table sorting script by Joost de Valk, check it out at http://www.joostdevalk.nl/code/sortable-table/.
|
||||||
|
Based on a script from http://www.kryogenix.org/code/browser/sorttable/.
|
||||||
|
Distributed under the MIT license: http://www.kryogenix.org/code/browser/licence.html .
|
||||||
|
|
||||||
|
Copyright (c) 1997-2007 Stuart Langridge, Joost de Valk. |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in |
||||||
|
all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
THE SOFTWARE. |
||||||
|
|
||||||
|
Version 1.5.7 |
||||||
|
*/ |
||||||
|
|
||||||
|
/* You can change these values */ |
||||||
|
var europeandate = true; |
||||||
|
var alternate_row_colors = true; |
||||||
|
|
||||||
|
/* Don't change anything below this unless you know what you're doing */ |
||||||
|
// addEvent(window, "load", sortables_init);
|
||||||
|
|
||||||
|
var SORT_COLUMN_INDEX; |
||||||
|
var thead = false; |
||||||
|
|
||||||
|
function alternate(table) { |
||||||
|
// Take object table and get all it's tbodies.
|
||||||
|
var i, j, tableBodies, tableRows; |
||||||
|
tableBodies = table.getElementsByTagName("tbody"); |
||||||
|
// Loop through these tbodies
|
||||||
|
for (i = 0; i < tableBodies.length; i += 1) { |
||||||
|
// Take the tbody, and get all it's rows
|
||||||
|
tableRows = tableBodies[i].getElementsByTagName("tr"); |
||||||
|
// Loop through these rows
|
||||||
|
// Start at 1 because we want to leave the heading row untouched
|
||||||
|
for (j = 0; j < tableRows.length; j += 1) { |
||||||
|
// Check if j is even, and apply classes for both possible results
|
||||||
|
if ((j % 2) === 0) { |
||||||
|
if (tableRows[j].className.indexOf('odd') !== -1) { |
||||||
|
tableRows[j].className = tableRows[j].className.replace('odd', 'even'); |
||||||
|
} else { |
||||||
|
if (tableRows[j].className.indexOf('even') === -1) { |
||||||
|
tableRows[j].className += " even"; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (tableRows[j].className.indexOf('even') !== -1) { |
||||||
|
tableRows[j].className = tableRows[j].className.replace('even', 'odd'); |
||||||
|
} else { |
||||||
|
if (tableRows[j].className.indexOf('odd') === -1) { |
||||||
|
tableRows[j].className += " odd"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function ts_getInnerText(el) { |
||||||
|
if (typeof el === "string") { |
||||||
|
return el; |
||||||
|
} |
||||||
|
if (typeof el === "undefined") { |
||||||
|
return el; |
||||||
|
} |
||||||
|
if (el.innerText) { |
||||||
|
return el.innerText; |
||||||
|
} //Not needed but it is faster
|
||||||
|
var str, cs, l, i; |
||||||
|
str = ""; |
||||||
|
|
||||||
|
cs = el.childNodes; |
||||||
|
l = cs.length; |
||||||
|
for (i = 0; i < l; i += 1) { |
||||||
|
switch (cs[i].nodeType) { |
||||||
|
case 1: //ELEMENT_NODE
|
||||||
|
str += ts_getInnerText(cs[i]); |
||||||
|
break; |
||||||
|
case 3: //TEXT_NODE
|
||||||
|
str += cs[i].nodeValue; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return str; |
||||||
|
} |
||||||
|
|
||||||
|
function ts_makeSortable(t) { |
||||||
|
var firstRow, i, cell, txt; |
||||||
|
if (t.rows && t.rows.length > 0) { |
||||||
|
if (t.tHead && t.tHead.rows.length > 0) { |
||||||
|
firstRow = t.tHead.rows[t.tHead.rows.length - 1]; |
||||||
|
thead = true; |
||||||
|
} else { |
||||||
|
firstRow = t.rows[0]; |
||||||
|
} |
||||||
|
} |
||||||
|
if (!firstRow) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// We have a first row: assume it's the header, and make its contents clickable links
|
||||||
|
for (i = 0; i < firstRow.cells.length; i += 1) { |
||||||
|
cell = firstRow.cells[i]; |
||||||
|
txt = ts_getInnerText(cell); |
||||||
|
if (cell.className !== "unsortable" && cell.className.indexOf("unsortable") === -1) { |
||||||
|
cell.innerHTML = '<a href="#" class="sortheader sort" onclick="ts_resortTable(this, ' + |
||||||
|
i + |
||||||
|
');return false;">' + |
||||||
|
txt + |
||||||
|
'</a>'; |
||||||
|
} |
||||||
|
} |
||||||
|
if (alternate_row_colors) { |
||||||
|
alternate(t); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function sortables_init() { |
||||||
|
// Find table with id sortable-table and make them sortable
|
||||||
|
if (!document.getElementById) { |
||||||
|
return; |
||||||
|
} |
||||||
|
var tbl = document.getElementById("sortable-table"); |
||||||
|
ts_makeSortable(tbl); |
||||||
|
} |
||||||
|
|
||||||
|
function getParent(el, pTagName) { |
||||||
|
if (el === null) { |
||||||
|
return null; |
||||||
|
} else if (el.nodeType === 1 && el.tagName.toLowerCase() === pTagName.toLowerCase()) { |
||||||
|
return el; |
||||||
|
} else { |
||||||
|
return getParent(el.parentNode, pTagName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function ts_get_cell_data(a, idx) { |
||||||
|
var acell, aa; |
||||||
|
if ((typeof idx) === "undefined") { |
||||||
|
acell = a.cells[SORT_COLUMN_INDEX]; |
||||||
|
} else { |
||||||
|
acell = a.cells[idx]; |
||||||
|
} |
||||||
|
if ((aa = acell.getAttribute("raw-data")) === null) { |
||||||
|
aa = ts_getInnerText(acell).toLowerCase(); |
||||||
|
} |
||||||
|
return aa; |
||||||
|
} |
||||||
|
|
||||||
|
function compare_numeric(a, b) { |
||||||
|
var af, bf; |
||||||
|
af = parseFloat(a); |
||||||
|
af = (isNaN(af) ? 0 : af); |
||||||
|
bf = parseFloat(b); |
||||||
|
bf = (isNaN(bf) ? 0 : bf); |
||||||
|
return af - bf; |
||||||
|
} |
||||||
|
|
||||||
|
function ts_sort_numeric(a, b) { |
||||||
|
var cells = [ts_get_cell_data(a), ts_get_cell_data(b)]; |
||||||
|
return compare_numeric(cells[0], cells[1]); |
||||||
|
} |
||||||
|
|
||||||
|
function ts_sort_caseinsensitive(a, b) { |
||||||
|
var cells = [ts_get_cell_data(a), ts_get_cell_data(b)]; |
||||||
|
if (cells[0] === cells[1]) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
if (cells[0] < cells[1]) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
function trim(s) { |
||||||
|
return s.replace(/^\s+|\s+$/g, ""); |
||||||
|
} |
||||||
|
|
||||||
|
function ts_resortTable(lnk, clid) { |
||||||
|
var td, column, t, first, itm, i, j, k, numeric_flag, all_sort_links, ci, firstRow, newRows, sortfn; |
||||||
|
td = lnk.parentNode; |
||||||
|
column = clid || td.cellIndex; |
||||||
|
t = getParent(td, 'TABLE'); |
||||||
|
|
||||||
|
// Do not sort single a row
|
||||||
|
if (t.rows.length <= 1) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Determine if all rows are equal
|
||||||
|
first = ts_get_cell_data(t.tBodies[0].rows[0], 0); |
||||||
|
itm = first; |
||||||
|
i = 0; |
||||||
|
while (itm === first && i < t.tBodies[0].rows.length) { |
||||||
|
itm = ts_get_cell_data(t.tBodies[0].rows[i], column); |
||||||
|
itm = trim(itm); |
||||||
|
if (itm.substr(0, 4) === "<!--" || itm.length === 0) { |
||||||
|
itm = ""; |
||||||
|
} |
||||||
|
i += 1; |
||||||
|
} |
||||||
|
if (itm === first) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Determine the sort type. You can set numeric=true on the header to force numeric sorting
|
||||||
|
sortfn = ts_sort_caseinsensitive; |
||||||
|
if (thead) { |
||||||
|
if (itm.match(/-?\d+(?:\.\d+)?/)) { |
||||||
|
sortfn = ts_sort_numeric; // Normal number
|
||||||
|
} |
||||||
|
numeric_flag = t.tHead.rows[0].cells[column].getAttribute("numeric"); |
||||||
|
if (numeric_flag === "true") { |
||||||
|
sortfn = ts_sort_numeric; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Delete any other arrows there may be showing
|
||||||
|
all_sort_links = $$("a.sortheader.sort"); |
||||||
|
for (ci = 0; ci < all_sort_links.length; ci += 1) { |
||||||
|
if (getParent(all_sort_links[ci], "table") === getParent(lnk, "table")) { // in the same table as us?
|
||||||
|
all_sort_links[ci].className = all_sort_links[ci].className.replace(" desc", "").replace(" asc", ""); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Do the sorting
|
||||||
|
SORT_COLUMN_INDEX = column; |
||||||
|
firstRow = []; |
||||||
|
newRows = []; |
||||||
|
for (k = 0; k < t.tBodies.length; k += 1) { |
||||||
|
for (i = 0; i < t.tBodies[k].rows[0].length; i += 1) { |
||||||
|
firstRow[i] = t.tBodies[k].rows[0][i]; |
||||||
|
} |
||||||
|
} |
||||||
|
for (k = 0; k < t.tBodies.length; k += 1) { |
||||||
|
if (!thead) { |
||||||
|
// Skip the first row
|
||||||
|
for (j = 1; j < t.tBodies[k].rows.length; j += 1) { |
||||||
|
newRows[j - 1] = t.tBodies[k].rows[j]; |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Do NOT skip the first row
|
||||||
|
for (j = 0; j < t.tBodies[k].rows.length; j += 1) { |
||||||
|
newRows[j] = t.tBodies[k].rows[j]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
newRows.sort(sortfn); |
||||||
|
if (lnk.getAttribute("sortdir") === 'down') { |
||||||
|
lnk.setAttribute('sortdir', 'up'); |
||||||
|
lnk.className += " asc"; |
||||||
|
} else { |
||||||
|
newRows.reverse(); |
||||||
|
lnk.setAttribute('sortdir', 'down'); |
||||||
|
lnk.className += " desc"; |
||||||
|
} |
||||||
|
// We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
|
||||||
|
// don't do sortbottom rows
|
||||||
|
for (i = 0; i < newRows.length; i += 1) { |
||||||
|
if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') === -1))) { |
||||||
|
t.tBodies[0].appendChild(newRows[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
// do sortbottom rows only
|
||||||
|
for (i = 0; i < newRows.length; i += 1) { |
||||||
|
if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') !== -1)) { |
||||||
|
t.tBodies[0].appendChild(newRows[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
alternate(t); |
||||||
|
} |
||||||
|
|
||||||
|
function addEvent(elm, evType, fn, useCapture) |
||||||
|
// addEvent and removeEvent
|
||||||
|
// cross-browser event handling for IE5+, NS6 and Mozilla
|
||||||
|
// By Scott Andrew
|
||||||
|
{ |
||||||
|
if (elm.addEventListener) { |
||||||
|
elm.addEventListener(evType, fn, useCapture); |
||||||
|
return true; |
||||||
|
} else if (elm.attachEvent) { |
||||||
|
var r = elm.attachEvent("on" + evType, fn); |
||||||
|
return r; |
||||||
|
} else { |
||||||
|
alert("Handler could not be removed"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function clean_num(str) { |
||||||
|
str = str.replace(/^[^\-?\d]+/, ""); |
||||||
|
str.replace(/(\d),(\d)/g, "$1$2"); |
||||||
|
return str; |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
.help { |
||||||
|
margin-left: 5px; |
||||||
|
margin-right: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.tooltip { |
||||||
|
position: absolute; |
||||||
|
margin-top: 3px; |
||||||
|
margin-bottom: 3px; |
||||||
|
padding: 3px; |
||||||
|
width: 400px; |
||||||
|
z-index: 256; |
||||||
|
color: #000000; |
||||||
|
border: 1px solid #000000; |
||||||
|
background: #FFFFCC; |
||||||
|
font: 12px Verdana, sans-serif; |
||||||
|
text-align: left; |
||||||
|
padding: -50px; |
||||||
|
line-height: 16px; |
||||||
|
font-size: 11px; |
||||||
|
white-space: normal; |
||||||
|
} |
||||||
|
|
||||||
|
.filter-icon { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.filter-tip { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.group-by-icon { |
||||||
|
float: right; |
||||||
|
margin-right: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.group-by-tip { |
||||||
|
margin-top: -300px; |
||||||
|
margin-left: -475px; |
||||||
|
} |
||||||
|
|
||||||
|
.filter-legend-icon { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.filter-legend-tip { |
||||||
|
margin-left: 10px; |
||||||
|
} |
||||||
|
|
||||||
|
.group_by-legend-icon { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.group_by-legend-tip { |
||||||
|
margin-left: 10px; |
||||||
|
} |
@ -0,0 +1,619 @@ |
|||||||
|
.cost_types { |
||||||
|
padding-bottom: 3px; |
||||||
|
} |
||||||
|
|
||||||
|
.cost_types a.active { |
||||||
|
color: #000; |
||||||
|
font-weight: bold; |
||||||
|
} |
||||||
|
|
||||||
|
.report { |
||||||
|
text-align: center; |
||||||
|
border-collapse: collapse; |
||||||
|
border: solid 1px #ccc !important; |
||||||
|
width: auto !important; |
||||||
|
} |
||||||
|
|
||||||
|
.report td, .report th { |
||||||
|
min-width: 90px; |
||||||
|
white-space: nowrap; |
||||||
|
} |
||||||
|
|
||||||
|
.report td { |
||||||
|
border: dotted 1px #ddd; |
||||||
|
color: #666; |
||||||
|
text-align: right; |
||||||
|
padding-right: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.report tbody th, .report tbody td, .inner { |
||||||
|
max-width: 300px; |
||||||
|
white-space: normal !important; |
||||||
|
} |
||||||
|
|
||||||
|
.report td:hover { |
||||||
|
color: #000; |
||||||
|
outline: #ccc 1px solid; |
||||||
|
outline-offset: 1px; |
||||||
|
} |
||||||
|
|
||||||
|
.report td.empty:hover { |
||||||
|
outline: none; |
||||||
|
} |
||||||
|
|
||||||
|
.report th { |
||||||
|
border: solid 1px #ccc; |
||||||
|
background-color: #e3e3e3; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
|
||||||
|
.report .odd th.inner { |
||||||
|
background-color: #e8e8e8; |
||||||
|
} |
||||||
|
|
||||||
|
.report .even th.inner { |
||||||
|
background-color: #e3e3e3; |
||||||
|
} |
||||||
|
|
||||||
|
.report th.inner { |
||||||
|
border: solid 1px #ccc; |
||||||
|
background-color: #efefef; |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
|
||||||
|
.report tr.even:hover .inner, |
||||||
|
.report tr.even:hover .bottom, |
||||||
|
.report tr.even:hover .empty, |
||||||
|
.report tr.even:hover .right { |
||||||
|
background-color: #f5f5c5 !important; |
||||||
|
} |
||||||
|
/* IE7 made me do it! */ |
||||||
|
.report tr.odd:hover .inner, |
||||||
|
.report tr.odd:hover .bottom, |
||||||
|
.report tr.odd:hover .empty, |
||||||
|
.report tr.odd:hover .right { |
||||||
|
background-color: #f5f5c5 !important; |
||||||
|
} |
||||||
|
|
||||||
|
.report .top { |
||||||
|
border-top-style: solid; |
||||||
|
border-top-color: #ccc; |
||||||
|
/* border-top: 2px solid #ccc !important; */ |
||||||
|
} |
||||||
|
|
||||||
|
.report .bottom { |
||||||
|
border-bottom-style: solid; |
||||||
|
border-bottom-color: #ccc; |
||||||
|
/* border-bottom: 2px solid #ccc !important; */ |
||||||
|
} |
||||||
|
|
||||||
|
.report td.penultimate { |
||||||
|
border-right-style: solid; |
||||||
|
} |
||||||
|
|
||||||
|
.report thead .inner, .report tfoot .inner { |
||||||
|
text-align: right; |
||||||
|
padding-right: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.report .result { |
||||||
|
font-size: 120%; |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
|
||||||
|
#result-table { |
||||||
|
margin-top: 10px !important; |
||||||
|
} |
||||||
|
|
||||||
|
.report thead tr:hover .inner, .report tfoot tr:hover .inner { |
||||||
|
background-color: #efefef; |
||||||
|
} |
||||||
|
|
||||||
|
.report .left { |
||||||
|
text-align: left !important; |
||||||
|
padding-left: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.report .right { |
||||||
|
text-align: right !important; |
||||||
|
padding-right: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
/* Details view*/ |
||||||
|
|
||||||
|
.detail-report td { |
||||||
|
text-align: left; |
||||||
|
vertical-align: top; |
||||||
|
} |
||||||
|
|
||||||
|
#query_form_content { |
||||||
|
width: 916px; |
||||||
|
} |
||||||
|
|
||||||
|
#query_form fieldset.header_collapsible.collapsible { |
||||||
|
padding-bottom: 10px; |
||||||
|
} |
||||||
|
|
||||||
|
/* Overwriting styling for headlines within the query. */ |
||||||
|
/* TODO: Font-size seems to be a bit odd. Needs some love. */ |
||||||
|
.new_report fieldset h3 { |
||||||
|
font-size: 1.17em; |
||||||
|
border: none; |
||||||
|
} |
||||||
|
|
||||||
|
.remove-box { |
||||||
|
height: 20px; |
||||||
|
width: 20px; |
||||||
|
background: white; |
||||||
|
float: right; |
||||||
|
padding: 1px 0 0 0; |
||||||
|
display: block; |
||||||
|
margin-top: -5px; |
||||||
|
margin-right: -2px; |
||||||
|
} |
||||||
|
|
||||||
|
.filter_rem { |
||||||
|
color: transparent; |
||||||
|
overflow: hidden; |
||||||
|
cursor: pointer; |
||||||
|
background: no-repeat center center transparent; |
||||||
|
height: 18px; |
||||||
|
width: 18px; |
||||||
|
border-style: none; |
||||||
|
display: block; |
||||||
|
margin: 0 0 0 2px; |
||||||
|
} |
||||||
|
|
||||||
|
.icon-filter-rem { |
||||||
|
background-image: url(../images/close.gif); |
||||||
|
position: relative; |
||||||
|
float: right; |
||||||
|
padding: 0px; |
||||||
|
} |
||||||
|
|
||||||
|
/***** icons *****/ |
||||||
|
/* |
||||||
|
We hide the original content of things with class 'icon' and disply an icon instead. |
||||||
|
This css class is optimized for usage report breadcrumbs. |
||||||
|
Hint: The content of the title attribute is shown as a tooltip if the underlying DOM element is an <a>. |
||||||
|
*/ |
||||||
|
.breadcrumb_icon { |
||||||
|
padding: 3px 5px 0 16px; |
||||||
|
cursor: pointer; |
||||||
|
background: no-repeat left center; |
||||||
|
border-style: none; |
||||||
|
display: inline-block; |
||||||
|
margin-top: 0px; |
||||||
|
margin-bottom: 0px; |
||||||
|
} |
||||||
|
|
||||||
|
.icon-edit { |
||||||
|
background-image: url(../images/edit.png); |
||||||
|
} |
||||||
|
.icon-delete { |
||||||
|
background-image: url(../images/delete.png); |
||||||
|
} |
||||||
|
|
||||||
|
#query-name-edit-button { |
||||||
|
margin-left: 16px; |
||||||
|
font-size: 12px; |
||||||
|
} |
||||||
|
|
||||||
|
.filter { |
||||||
|
-webkit-border-radius: 5px; |
||||||
|
-moz-border-radius: 5px; |
||||||
|
border-radius: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.inactive-filter { |
||||||
|
background-color: #FCE29A !important; |
||||||
|
} |
||||||
|
|
||||||
|
.dependent-filter-label { |
||||||
|
margin-left: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
.filter_values { |
||||||
|
white-space: nowrap; |
||||||
|
} |
||||||
|
|
||||||
|
.filter_radio_option { |
||||||
|
padding-left: 5px; |
||||||
|
padding-right: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
#add_filter_block { |
||||||
|
margin-top: 6px; |
||||||
|
} |
||||||
|
|
||||||
|
#add_filter_select { |
||||||
|
margin-bottom: 10px; |
||||||
|
} |
||||||
|
|
||||||
|
fieldset#filters table tr.filter:hover { |
||||||
|
background: #aaa; |
||||||
|
} |
||||||
|
|
||||||
|
fieldset#filters table tr.filter { |
||||||
|
color: #000; |
||||||
|
background: #ededed; |
||||||
|
-webkit-border-radius: 5px; |
||||||
|
-moz-border-radius: 5px; |
||||||
|
border-radius: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
/* Aligning filter elements at the top. */ |
||||||
|
fieldset#filters table td { |
||||||
|
vertical-align: top; |
||||||
|
border-spacing: 5px 5px; |
||||||
|
border-color: white; |
||||||
|
border-style: solid; |
||||||
|
border-width: 2px 0px 0px; |
||||||
|
padding: 5px 2px 5px 2px; |
||||||
|
} |
||||||
|
|
||||||
|
fieldset#filters table .no-border td { |
||||||
|
border-style: none !important; |
||||||
|
} |
||||||
|
|
||||||
|
fieldset#filters table td > label { |
||||||
|
left: 3px; |
||||||
|
top: 3px; |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
/* ----- group by --- */ |
||||||
|
#group_by_area { |
||||||
|
margin: 5px 0 10px 0; |
||||||
|
} |
||||||
|
|
||||||
|
.in_row { |
||||||
|
float: left; |
||||||
|
display: block; |
||||||
|
list-style: none; |
||||||
|
border-width: 0px; |
||||||
|
} |
||||||
|
|
||||||
|
.group_by_element { |
||||||
|
margin-left: -15px; |
||||||
|
cursor: move; |
||||||
|
} |
||||||
|
|
||||||
|
/* have #content here to overwrite line-heigth of other themes through being more specific*/ |
||||||
|
#content .group_by_label { |
||||||
|
margin: 0px; |
||||||
|
padding: 0px 18px 0 0; |
||||||
|
min-width: 60px; |
||||||
|
text-align: center; |
||||||
|
white-space: nowrap; |
||||||
|
font-weight: bold; |
||||||
|
color: #fff; |
||||||
|
background-color: #9A9A9A; |
||||||
|
height: 22px; |
||||||
|
line-height: 22px; |
||||||
|
cursor: move; |
||||||
|
} |
||||||
|
|
||||||
|
.group_by_label.hover { |
||||||
|
background-color: #009999 !important; |
||||||
|
} |
||||||
|
|
||||||
|
.group_by_remove { |
||||||
|
background-color: #9A9A9A; |
||||||
|
height: 7px; |
||||||
|
width: 8px; |
||||||
|
position: absolute; |
||||||
|
top: 7px; |
||||||
|
right: 21px; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.group_by_remove.hover { |
||||||
|
background-color: #009999 !important; |
||||||
|
} |
||||||
|
|
||||||
|
/* Overwrite normal h3 definition - a h-tag is needed for accessibility purposes */ |
||||||
|
h3.reporting_formatting { |
||||||
|
font-weight:normal; |
||||||
|
font-size: 13px; |
||||||
|
margin:0px; |
||||||
|
padding:0px; |
||||||
|
color:#333; |
||||||
|
border:0px; |
||||||
|
} |
||||||
|
|
||||||
|
/* have #content here to overwrite line-heigth of other themes through being more specific*/ |
||||||
|
#content .group_by_caption { |
||||||
|
color: #FFFFFF; |
||||||
|
background-color: #4B4B4B; |
||||||
|
font-weight: bold; |
||||||
|
padding: 0 7px; |
||||||
|
margin: 0; |
||||||
|
height: 22px; |
||||||
|
line-height: 22px; |
||||||
|
min-width: 55px; |
||||||
|
} |
||||||
|
|
||||||
|
.arrow { |
||||||
|
height: 0; |
||||||
|
width: 0; |
||||||
|
border-style: solid; |
||||||
|
border-width: 11px 11px 11px 7px; |
||||||
|
} |
||||||
|
|
||||||
|
.arrow_left { |
||||||
|
border-color: #9A9A9A #9A9A9A #9A9A9A transparent; |
||||||
|
} |
||||||
|
|
||||||
|
.arrow_left.hover { |
||||||
|
border-color: #009999 #009999 #009999 transparent !important; |
||||||
|
} |
||||||
|
|
||||||
|
.arrow_right { |
||||||
|
border-color: transparent transparent transparent #9A9A9A; |
||||||
|
} |
||||||
|
|
||||||
|
.arrow_right.hover { |
||||||
|
border-color: transparent transparent transparent #009999 !important; |
||||||
|
} |
||||||
|
|
||||||
|
.arrow_group_by_caption { |
||||||
|
border-color: transparent transparent transparent #4B4B4B; |
||||||
|
} |
||||||
|
|
||||||
|
.drag_container { |
||||||
|
padding: 0; |
||||||
|
height: 22px; |
||||||
|
margin-bottom: 1em; |
||||||
|
background-color: #EEE; |
||||||
|
} |
||||||
|
|
||||||
|
.drag_container select { |
||||||
|
margin-right: 3px; |
||||||
|
float: right; |
||||||
|
} |
||||||
|
|
||||||
|
.drag_target { |
||||||
|
min-height: 10px; |
||||||
|
} |
||||||
|
|
||||||
|
.drag_container_accept { |
||||||
|
background-color: #F5F5C5; |
||||||
|
} |
||||||
|
|
||||||
|
/* -- end group-by -- */ |
||||||
|
|
||||||
|
td .drill_down, th .drill_down { |
||||||
|
font-size: 8px; |
||||||
|
display: block; |
||||||
|
float: right; |
||||||
|
font-weight: bold; |
||||||
|
visibility: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
td:hover .drill_down, th:hover .drill_down { |
||||||
|
visibility: visible; |
||||||
|
} |
||||||
|
|
||||||
|
/*Buttons*/ |
||||||
|
|
||||||
|
.buttons .reporting_button { |
||||||
|
background-color: #008BD0; |
||||||
|
border: none; |
||||||
|
cursor: pointer; |
||||||
|
margin: 0px; |
||||||
|
overflow: visible; |
||||||
|
text-align: center; |
||||||
|
white-space: nowrap; |
||||||
|
-moz-border-radius: 3px; |
||||||
|
border-radius: 3px; |
||||||
|
padding: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.form_controls { |
||||||
|
margin-top: 6px; |
||||||
|
} |
||||||
|
|
||||||
|
.form_controls > * { |
||||||
|
margin: 0 5px 0 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.buttons .reporting_button:hover { |
||||||
|
background-color: #24B3E7; |
||||||
|
} |
||||||
|
|
||||||
|
.buttons .reporting_button * em { |
||||||
|
color: white; |
||||||
|
display: block; |
||||||
|
font-style: normal; |
||||||
|
font-weight: bold; |
||||||
|
line-height: 17px; |
||||||
|
} |
||||||
|
|
||||||
|
.buttons .reporting_button > * { |
||||||
|
display: inline-block; |
||||||
|
margin: 0px; |
||||||
|
} |
||||||
|
|
||||||
|
.saved_queries .reporting_button { |
||||||
|
float: left; |
||||||
|
margin: 2px; |
||||||
|
} |
||||||
|
|
||||||
|
.buttons .button { |
||||||
|
border: none; |
||||||
|
cursor: pointer; |
||||||
|
margin: 0px; |
||||||
|
margin-right: 4px; |
||||||
|
overflow: visible; |
||||||
|
text-align: center; |
||||||
|
white-space: nowrap; |
||||||
|
-moz-border-radius: 3px; |
||||||
|
border-radius: 3px; |
||||||
|
padding: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.buttons .apply { |
||||||
|
background-color: #008BD0; |
||||||
|
} |
||||||
|
|
||||||
|
.buttons .apply:hover { |
||||||
|
background-color: #24B3E7; |
||||||
|
} |
||||||
|
|
||||||
|
.buttons .secondary { |
||||||
|
background-color: #9a9a9a; |
||||||
|
} |
||||||
|
|
||||||
|
.buttons .secondary:hover { |
||||||
|
background-color: #666666; |
||||||
|
} |
||||||
|
|
||||||
|
.buttons .button span em { |
||||||
|
color: white; |
||||||
|
display: block; |
||||||
|
font-style: normal; |
||||||
|
font-weight: bold; |
||||||
|
line-height: 17px; |
||||||
|
} |
||||||
|
|
||||||
|
.buttons .button span { |
||||||
|
display: inline-block; |
||||||
|
margin: 3px; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.buttons .button-icon { |
||||||
|
padding: 0px 5px 0 16px; |
||||||
|
cursor: pointer; |
||||||
|
background: no-repeat left center; |
||||||
|
border-style: none; |
||||||
|
display: inline-block; |
||||||
|
margin-top: 0px; |
||||||
|
margin-bottom: 0px; |
||||||
|
} |
||||||
|
|
||||||
|
.button .icon-save { |
||||||
|
background-image: url(../images/disk.gif); |
||||||
|
} |
||||||
|
|
||||||
|
.button .icon-save-as { |
||||||
|
background-image: url(../images/disks.gif); |
||||||
|
} |
||||||
|
|
||||||
|
.button .icon-delete { |
||||||
|
background-image: url(../images/delete.gif); |
||||||
|
} |
||||||
|
|
||||||
|
.button .icon-clear { |
||||||
|
background-image: url(../images/remove.gif); |
||||||
|
} |
||||||
|
|
||||||
|
div.button_form { |
||||||
|
/* TODO IE Compatibility! */ |
||||||
|
background-color: white; |
||||||
|
border: 1px solid gray; |
||||||
|
-moz-border-radius: 3px; |
||||||
|
border-radius: 3px; |
||||||
|
left: 100px; |
||||||
|
position: absolute; |
||||||
|
padding: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
div.button_form * input#name { |
||||||
|
width: 200px |
||||||
|
} |
||||||
|
|
||||||
|
div.button_form * input[type="button"] { |
||||||
|
float: right; |
||||||
|
} br { clear: right; } |
||||||
|
|
||||||
|
div.button_form p * { |
||||||
|
margin: 2px; |
||||||
|
} |
||||||
|
|
||||||
|
/***** Save and Delete Reports ****/ |
||||||
|
#save_as_form, #delete_form { |
||||||
|
z-index: 999; |
||||||
|
} |
||||||
|
|
||||||
|
#progressbar, .hidden { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
#progressbar_container { |
||||||
|
width: 300px; |
||||||
|
height: 16px; |
||||||
|
border: 1px solid #ccc; |
||||||
|
padding: 0; |
||||||
|
margin: 0; |
||||||
|
position: relative; |
||||||
|
background-color: #9A9A9A; |
||||||
|
background-repeat: repeat-x; |
||||||
|
} |
||||||
|
|
||||||
|
#progressbar_container div { |
||||||
|
background-color:#fff; |
||||||
|
} |
||||||
|
|
||||||
|
/***** Ajax indicator ******/ |
||||||
|
#ajax-indicator { |
||||||
|
font-family: Verdana, sans-serif; |
||||||
|
position: absolute; /* fixed not supported by IE */ |
||||||
|
background-color:#eee; |
||||||
|
border: 1px solid #bbb; |
||||||
|
top:35%; |
||||||
|
left:40%; |
||||||
|
width:20%; |
||||||
|
font-weight:bold; |
||||||
|
text-align:center; |
||||||
|
padding:0.6em; |
||||||
|
z-index:100; |
||||||
|
filter:alpha(opacity=50); |
||||||
|
opacity: 0.5; |
||||||
|
line-height: 10px; |
||||||
|
font-size: 18px; |
||||||
|
} |
||||||
|
|
||||||
|
html>body #ajax-indicator { position: fixed; } |
||||||
|
|
||||||
|
#ajax-indicator span { |
||||||
|
background-position: 0% 40%; |
||||||
|
background-repeat: no-repeat; |
||||||
|
background-image: url(../images/loading.gif); |
||||||
|
padding-left: 26px; |
||||||
|
vertical-align: bottom; |
||||||
|
} |
||||||
|
|
||||||
|
/* Calendar Fixes */ |
||||||
|
div.calendar /* let calendar expand properly */ |
||||||
|
{ |
||||||
|
font-size: medium; |
||||||
|
line-height: normal; |
||||||
|
} |
||||||
|
|
||||||
|
div.calendar table div { /* make sure the nested divs are large enough, too */ |
||||||
|
font-size: medium; |
||||||
|
line-height: normal; |
||||||
|
} |
||||||
|
|
||||||
|
.calendar .combo .label, .calendar .combo .label-IEfix { /* set the proper size for the combo boxes with months and years */ |
||||||
|
font-size: 10px; |
||||||
|
line-height: normal; |
||||||
|
} |
||||||
|
|
||||||
|
.calendar tbody .day { /* avoid jitter during quick mouse-overs over days */ |
||||||
|
border: 1px dotted transparent; |
||||||
|
padding: 1px 3px 1px 1px; |
||||||
|
} |
||||||
|
|
||||||
|
/* Accessibility specific styles */ |
||||||
|
fieldset#filters table td > label.hidden-for-sighted, .hidden-for-sighted { |
||||||
|
position:absolute; |
||||||
|
left:-10000px; |
||||||
|
top:auto; |
||||||
|
width:1px; |
||||||
|
height:1px; |
||||||
|
overflow:hidden; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,5 @@ |
|||||||
|
module OpenProject |
||||||
|
module ReportingEngine |
||||||
|
require "open_project/reporting_engine/engine" |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,33 @@ |
|||||||
|
require 'rails/engine' |
||||||
|
|
||||||
|
module OpenProject::ReportingEngine |
||||||
|
class Engine < ::Rails::Engine |
||||||
|
engine_name :openproject_reportingengine |
||||||
|
|
||||||
|
config.autoload_paths += Dir["#{config.root}/lib/"] |
||||||
|
|
||||||
|
initializer 'reportingengine.precompile_assets' do |
||||||
|
Rails.application.config.assets.precompile += %w(reportingengine.css reportingengine.js) |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
config.to_prepare do |
||||||
|
require_dependency 'open_project/reporting_engine/patches' |
||||||
|
require_dependency 'open_project/reporting_engine/patches/big_decimal_patch' |
||||||
|
require_dependency 'open_project/reporting_engine/patches/to_date_patch' |
||||||
|
end |
||||||
|
|
||||||
|
config.after_initialize do |
||||||
|
Redmine::Plugin.register :openproject_reportingengine do |
||||||
|
name 'OpenProject ReportingEngine' |
||||||
|
author 'Finn GmbH' |
||||||
|
description 'A plugin for reports' |
||||||
|
|
||||||
|
url 'https://github.com/finnlabs/openproject_reportingengine' |
||||||
|
author_url 'http://www.finn.de/' |
||||||
|
|
||||||
|
version OpenProject::ReportingEngine::VERSION |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,2 @@ |
|||||||
|
module OpenProject::ReportingEngine::Patches |
||||||
|
end |
@ -1,4 +1,4 @@ |
|||||||
module BigDecimalPatch |
module OpenProject::ReportingEngine::Patches::BigDecimalPatch |
||||||
module BigDecimal |
module BigDecimal |
||||||
::BigDecimal.send :include, self |
::BigDecimal.send :include, self |
||||||
def to_d; self end |
def to_d; self end |
@ -1,6 +1,6 @@ |
|||||||
require 'date' |
require 'date' |
||||||
|
|
||||||
module ToDatePatch |
module OpenProject::ReportingEngine::Patches::ToDatePatch |
||||||
module StringAndNil |
module StringAndNil |
||||||
::String.send(:include, self) |
::String.send(:include, self) |
||||||
::NilClass.send(:include, self) |
::NilClass.send(:include, self) |
@ -1,4 +1,4 @@ |
|||||||
require 'reporting_engine/engine' |
require_dependency 'open_project/reporting_engine/engine_module' |
||||||
|
|
||||||
class Report < ActiveRecord::Base |
class Report < ActiveRecord::Base |
||||||
extend ProactiveAutoloader |
extend ProactiveAutoloader |
@ -1,5 +1,5 @@ |
|||||||
require 'set' |
require 'set' |
||||||
require 'reporting_engine/report' |
require_dependency 'open_project/reporting_engine/report' |
||||||
|
|
||||||
module Report::InheritedAttribute |
module Report::InheritedAttribute |
||||||
include Report::QueryUtils |
include Report::QueryUtils |
@ -0,0 +1,5 @@ |
|||||||
|
module OpenProject |
||||||
|
module ReportingEngine |
||||||
|
VERSION = '0.0.1' |
||||||
|
end |
||||||
|
end |
@ -1,5 +1,5 @@ |
|||||||
require 'reporting_engine/reporting_helper' |
require_dependency 'open_project/reporting_engine/helpers/reporting_helper' |
||||||
require 'reporting_engine/proactive_autoloader' |
require_dependency 'open_project/reporting_engine/proactive_autoloader' |
||||||
|
|
||||||
|
|
||||||
class Widget < ActionView::Base |
class Widget < ActionView::Base |