[ADD] Dashboard using jquery sortable portlet.

bzr revid: vda@tinyerp.com-20110511083134-xd05zedbaf5ct18h
This commit is contained in:
vda (OpenERP) 2011-05-11 14:01:34 +05:30
parent 2f5eda9403
commit c374a0d801
7 changed files with 103 additions and 1309 deletions

View File

@ -3,9 +3,8 @@
"version": "2.0",
"depends": ['base'],
"js": [
'static/lib/jquery.dashboard/js/jquery.dashboard.js',
'static/src/js/dashboard.js'
],
"css": ['static/lib/jquery.dashboard/css/dashboardui.css'],
"css": ['static/src/css/dashboard.css'],
'active': True
}

View File

@ -1 +0,0 @@
content 4 content 4 content 4 content 4 content 4 content 4 content 4 content 4 content 4 content 4 content 4

View File

@ -1,261 +0,0 @@
.ui-icon {
cursor:default;
}
.widgetheader {
cursor:move;
}
#layout-dialog .selected {
background: no-repeat scroll 0px -51px transparent;
}
#layout-dialog .layoutchoice {
width: 82px;
height: 51px;
float:left;
list-style-type:none;
margin:5px;
padding:0;
}
.selectedcolumn {
border: 3px dashed #aaaaaa;
}
.emptycolumn {
font-size:20px;
font-weight:bold;
color: #aaaaaa;
padding: 5px 5px 5px 5px;
}
.dashboard {
margin-top: 5px;
}
.right {
float:right;
}
.widgetheader {
padding:2px 2px 5px 5px;
}
.ui-icon {
float:left;
}
.widgetcontent {
padding:2px 2px 5px 5px;
}
.widget {
margin-bottom:10px;
}
.column {
float:left;
margin:0 1% -1.5em;
padding:0;
width:47.5%;
}
.layout-a .column {
width:98%;
}
.layout-a .column.second, .layout-a .column.third {
display:none;
}
.layout-aa .column {
width:47.5%;
}
.layout-aa .third {
display:none;
}
.layout-ba .column {
width:68%;
}
.layout-ba .first {
width:27%;
}
.layout-ba .third {
display:none;
}
.layout-ab .column {
width:27%;
}
.layout-ab .first {
width:68%;
}
.layout-ab .third {
display:none;
}
.layout-aaa .column {
width:30.9%;
}
/*body {
margin:0;
color:#333333;
font:12px/1.4 arial,FreeSans,Helvetica,sans-serif;
}*/
.headerlink {
color:#ffffff;
}
.headerlinks {
text-align:right;
margin-right:20px;
line-height:24px;
text-decoration:underline;
font-weight:bold;
}
.headerbox {
height:148px;
}
.ui-widget-overlay {
opacity:0.5;
}
/*.loading {
padding: 50px;
text-align: center;
background-image:loading.gif;
}*/
#layout-dialog ul {
margin:0;
padding:0;
}
#layout-dialog ul li a, #layout-dialog ul li a:link, #layout-dialog ul li a:visited {
border:1px solid #BBBBBB;
display:block;
float:left;
margin:0 1em 1em 0;
outline:medium none;
padding:0.35em;
width:auto;
}
.hidden {
display:none;
}
.controls {
border:1px solid #BBBBBB;
float:none;
margin:0;
padding:4px 0;
}
.controls {
width:100px;
background:none repeat scroll 0 0 #dddddd;
border:1px solid #6A8EB3;
color:#000000;
margin-top:-1px;
padding:4px 0;
position:absolute;
right:0;
z-index:2003;
}
.controls li {
float:none;
margin:0;
padding:0;
list-style-type:none;
margin:0 0 0 0.2em;
width:auto;
position:static;
}
.controls li a {
color:#000000;
font-weight:normal;
float:none;
margin:0;
text-decoration:none;
width:auto;
padding-left:5px;
}
.hiddenmenu {
position:relative;
}
.dialog .categories {
list-style:none outside none;
height: 414px;
}
.dialog .categories li.selected button {
color:#FFFFFF;
font-weight:800;
}
.dialog .categories li button {
background:none repeat scroll 0 0 transparent;
border:medium none;
color:#666666;
font-family:"segoe ui",helvetica,arial,sans-serif;
font-size:0.8em;
padding:0.4em 1.2em;
text-align:left;
width:100%;
}
ul.categories button {
cursor:pointer;
}
.widgetitem {
border:2px none white;
float:left;
font-size:0.77em;
height:142px;
margin:0;
overflow:hidden;
padding:0 20px 0 142px;
width:152px;
}
ol.widgets {
float:left;
list-style:none outside none;
margin:-10px;
padding:0;
width:auto;
}
.dialog .panel-body {
overflow:auto;
padding:10px;
}
.dialog .categories {
background:none repeat scroll 0 0 #FFFFFF;
border-right:1px solid #F0F0F0;
float:left;
height:100%;
list-style:none outside none;
margin:0 1.17em 0 0;
padding:10px 0 0;
width:25%;
}
.dialog .categories li.selected {
background:none repeat scroll 0 0 #6699CC;
color:#FFFFFF;
}
.widgetitem .add-button {
float:left;
margin:81px 0 0 -131px;
width:auto;
}
.widgetitem h3 {
margin:11px 0 0;
padding:0;
}
.widgetitem img {
border:1px solid #999999;
float:left;
margin:10px 0 0 -132px;
}

View File

@ -1,936 +0,0 @@
/*
* dashboard 1.0
* http://www.gxdeveloperweb.com/dashboard/
*
* Copyright (c) 2010 Mark Machielsen
*
* Dual licensed under the MIT and GPL licenses (same as jQuery):
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function($) { // Create closure.
// Constructor for dashboard object.
$.fn.dashboard = function(options) {
// Public properties of dashboard.
var dashboard = {};
var loading;
var widgetDirectoryUrl;
dashboard.layout;
dashboard.element = this;
dashboard.id = this.attr("id");
dashboard.widgets = {};
dashboard.widgetsToAdd = {};
dashboard.widgetCategories = {};
dashboard.initialized = false;
// Public methods
dashboard.serialize = function() {
dashboard.log('entering serialize function',1);
var r = '{"layout": "' + dashboard.layout.id + '", "data" : [';
// add al widgets in the right order
var i=0;
if ($('.' + opts.columnClass).length == 0) dashboard.log(opts.columnClass + ' class not found',5);
$('.' + opts.columnClass).each(function() {
$(this).children().each(function() {
if ($(this).hasClass(opts.widgetClass)) {
if (i > 0) { r+= ','; }
r+= (dashboard.getWidget($(this).attr("id"))).serialize();
i++;
}
});
});
r+= ']}';
return r;
}
dashboard.log = function(msg, level) {
if (level >= opts.debuglevel && typeof console != 'undefined') {
var l = '';
if (level == 1) l = 'INFO';
if (level == 2) l = 'EVENT';
if (level == 3) l = 'WARNING';
if (level == 5) l = 'ERROR';
console.log(l + ' - ' + msg);
}
}
dashboard.setLayout = function(layout) {
if (layout != null) {
dashboard.log('entering setLayout function with layout ' + layout.id,1);
} else {
dashboard.log('entering setLayout function with layout null',1);
}
dashboard.layout = layout;
loading.remove();
if (dashboard.layout != null) {
if (typeof opts.layoutClass != 'undefined') {
this.element.find('.' + opts.layoutClass).addClass(dashboard.layout.classname);
} else {
this.element.html(dashboard.layout.html);
}
}
// make the columns sortable, see http://jqueryui.com/demos/sortable/ for explaination
$('.' + opts.columnClass).sortable({
connectWith: $('.' + opts.columnClass),
opacity: opts.opacity,
handle: '.' + opts.widgetHeaderClass,
over: function(event, ui) {
$(this).addClass("selectedcolumn");
},
out: function(event, ui) {
$(this).removeClass("selectedcolumn");
},
receive: function(event, ui) {
// update the column attribute for the widget
var w = dashboard.getWidget(ui.item.attr("id"));
w.column = getColumnIdentifier($(this).attr("class"));
dashboard.log('dashboardStateChange event thrown for widget ' + w.id,2);
dashboard.element.trigger("dashboardStateChange",{"stateChange":"widgetMoved","widget":w});
dashboard.log('widgetDropped event thrown for widget ' + w.id,2);
w.element.trigger("widgetDropped",{"widget":w});
},
deactivate: function(event, ui) {
// This event is called for each column
dashboard.log('Widget is dropped: check if the column is now empty.',1);
var childLength = $(this).children().length;
if (childLength == 0) {
dashboard.log('adding the empty text to the column',1);
$(this).html('<div class="emptycolumn">' + opts.emptyColumnHtml + '</div>');
} else {
if (childLength == 2) {
// remove the empty column HTML
$(this).find('.emptycolumn').remove();
}
}
},
start: function(event, ui) {
ui.item.find('.' + opts.widgetTitleClass).addClass('noclick');
},
stop: function(event, ui) {
//sorting changed (within one list)
setTimeout(function(){
ui.item.find('.' + opts.widgetTitleClass).removeClass('noclick');
}, 300);
}
});
fixSortableColumns();
// trigger the dashboardLayoutLoaded event
dashboard.log('dashboardLayoutLoaded event thrown',2);
dashboard.element.trigger("dashboardLayoutLoaded");
}
// This is a workaround for the following problem: when I drag a widget from column2 to column1, sometimes the widget is
// moved to column3, which is not visible
function fixSortableColumns() {
dashboard.log('entering fixSortableColumns function',1);
$('.nonsortablecolumn').removeClass('nonsortablecolumn').addClass(opts.columnClass);
$('.' + opts.columnClass).filter(function() {return $(this).css("display") == 'none'}).addClass('nonsortablecolumn').removeClass(opts.columnClass);
}
function getColumnIdentifier(classes) {
dashboard.log('entering getColumnIdentifier function',1);
var r;
var s = classes.split(" ");
for (var i = 0;i < s.length;i++) {
if (s[i].indexOf(opts.columnPrefix) === 0) { r = s[i] };
};
return r.replace(opts.columnPrefix,'');
}
dashboard.loadLayout = function() {
dashboard.log('entering loadLayout function',1);
if (typeof opts.json_data.url != 'undefined' && opts.json_data.url.length > 0) {
// ajax option
dashboard.log('Getting JSON feed : ' + opts.json_data.url,1);
$.getJSON(opts.json_data.url, function(json) {
if (json == null) {
alert('Unable to get json. If you are using chrome: there is an issue with loading json with local files. It works on a server :-)',5);
return;
}
// set the layout
var currentLayout = (typeof dashboard.layout != 'undefined') ? dashboard.layout : getLayout(json.layout);
dashboard.setLayout(currentLayout);
dashboard.loadWidgets(json.data);
});
} else {
// set the layout
var currentLayout = (typeof dashboard.layout != 'undefined') ? dashboard.layout : getLayout(opts.json_data.layout);
dashboard.setLayout(currentLayout);
dashboard.loadWidgets(opts.json_data.data);
}
};
dashboard.addWidget = function(obj, column) {
dashboard.log('entering addWidget function',1);
// add the widget to the column
var wid = obj.id;
// check if the widget is already registered and available in the dom
if (typeof dashboard.widgets[wid] != 'undefined' && $('#' + wid).length > 0) {
var wi = $('#' + wid);
column = dashboard.widgets[wid].column;
// add it to the column
wi.appendTo(column);
} else {
// build the widget
dashboard.log('Applying template : ' + opts.widgetTemplate,1);
if ($('#' + opts.widgetTemplate).length == 0) dashboard.log('Template "' + opts.widgetTemplate + ' not found',5);
var widgetStr = tmpl($('#' + opts.widgetTemplate).html(), obj);
var wi = $(widgetStr);
// add it to the column
wi.appendTo(column);
if(column.find('.emptycolumn').length)
column.find('.emptycolumn').remove();
dashboard.widgets[wid] = widget({
id: wid,
element: wi,
column: obj.column,
url: (typeof obj.url != 'undefined' ? obj.url : null),
editurl: obj.editurl,
title: obj.title,
open: obj.open,
metadata: obj.metadata
});
}
dashboard.log('widgetAdded event thrown for widget ' + wid,2);
dashboard.widgets[wid].element.trigger("widgetAdded", {"widget":dashboard.widgets[wid]});
if (dashboard.initialized) {
dashboard.log('dashboardStateChange event thrown for widget ' + wid,2);
dashboard.element.trigger("dashboardStateChange",{"stateChange":"widgetAdded","widget":wi});
}
}
dashboard.loadWidgets = function(data) {
dashboard.log('entering loadWidgets function',1);
dashboard.element.find('.' + opts.columnClass).empty();
// This is for the manual feed
$(data).each(function() {
var column = this.column;
dashboard.addWidget(this, dashboard.element.find('.' + opts.columnPrefix + column));
}); // end loop for widgets
// check if there are widgets in the temp dashboard which needs to be moved
// this is not the correct place, but otherwise we are too late
// check if there are still widgets in the temp
$('#tempdashboard').find('.' + opts.widgetClass).each(function() {
// append it to the first column
var firstCol = dashboard.element.find('.' + opts.columnClass + ':first');
$(this).appendTo(firstCol);
// set the new column
dashboard.getWidget($(this).attr("id")).column = firstCol.attr("id");
});
$('#tempdashboard').remove();
// add the text to the empty columns
$('.' + opts.columnClass).each(function() {
if ($(this).children().length == 0) {
$(this).html('<div class="emptycolumn">' + opts.emptyColumnHtml + '</div>');
}
});
dashboard.initialized = true;
};
dashboard.init = function() {
dashboard.log('entering init function',1);
// load the widgets as fast as we can. After that add the binding
dashboard.loadLayout();
}
dashboard.getWidget = function(id) {
dashboard.log('entering getWidget function',1);
var wi = dashboard.widgets[id];
if (typeof wi != 'undefined') {
return wi;
} else {
return null;
}
}
// Merge in the caller's options with the defaults.
var opts = $.extend({}, $.fn.dashboard.defaults, options);
var addOpts = $.extend({}, $.fn.dashboard.defaults.addWidgetSettings, options.addWidgetSettings);
var layoutOpts = $.extend({}, $.fn.dashboard.defaults.editLayoutSettings, options.editLayoutSettings);
// Execution 'forks' here and restarts in init(). Tell the user we're busy with a loading.
var loading = $(opts.loadingHtml).appendTo(dashboard.element);
/**
* widget object
* Private sub-class of dashboard
* Constructor starts
*/
function widget(widget) {
dashboard.log('entering widget constructor',1);
// Merge default options with the options defined for this widget.
widget = $.extend({}, $.fn.dashboard.widget.defaults, widget);
// public functions
widget.openContent = function() {
// hide the open link, show the close link
widget.element.find('.widgetOpen').hide();
widget.element.find('.widgetClose').show();
dashboard.log('entering openContent function',1);
widget.open = true;
if (!widget.loaded) {
// load the content in the widget if the state == open
if (this.url != '' && this.url != null && typeof this.url != 'undefined') {
// add the loading
$(opts.loadingHtml).appendTo(widget.element.find('.' + opts.widgetContentClass));
dashboard.log('widgetShow event thrown for widget ' + widget.id,2);
widget.element.trigger("widgetShow", {"widget":widget});
widget.element.find('.' + opts.widgetContentClass).load(this.url, function(response, status, xhr) {
if (status == "error") {
widget.element.find('.' + opts.widgetContentClass).html(opts.widgetNotFoundHtml);
}
widget.loaded = true;
dashboard.log('widgetLoaded event thrown for widget ' + widget.id,2);
widget.element.trigger("widgetLoaded", {"widget":widget});
});
} else {
dashboard.log('widgetShow event thrown for widget ' + widget.id,2);
widget.element.trigger("widgetShow", {"widget":widget});
dashboard.log('widgetLoaded event thrown',2);
widget.element.trigger("widgetLoaded", {"widget":widget});
}
} else {
dashboard.log('widgetShow event thrown for widget ' + widget.id,2);
widget.element.trigger("widgetShow", {"widget":widget});
}
if (dashboard.initialized) {
dashboard.log('dashboardStateChange event thrown for widget ' + widget.id,2);
dashboard.element.trigger("dashboardStateChange",{"stateChange":"widgetOpened","widget":widget});
}
};
widget.refreshContent = function() {
dashboard.log('entering refreshContent function',1);
widget.loaded = false;
if (widget.open) {
widget.openContent();
}
}
widget.setTitle = function(newTitle){
dashboard.log('entering setTitle function',1);
widget.title=newTitle;
widget.element.find('.' + opts.widgetTitleClass).html(newTitle);
if (dashboard.initialized) {
dashboard.log('dashboardStateChange event thrown for widget ' + widget.id,2);
dashboard.element.trigger("dashboardStateChange",{"stateChange":"titleChanged","widget":widget});
}
}
widget.closeContent = function() {
dashboard.log('entering closeContent function',1);
widget.open = false;
dashboard.log('widgetHide event thrown for widget ' + widget.id,2);
widget.element.trigger("widgetHide", {"widget":widget});
// show the open link, hide the close link
widget.element.find('.widgetOpen').show();
widget.element.find('.widgetClose').hide();
dashboard.log('dashboardStateChange event thrown for widget ' + widget.id,2);
dashboard.element.trigger("dashboardStateChange",{"stateChange":"widgetClosed","widget":widget});
};
widget.addMetadataValue = function(name, value) {
dashboard.log('entering addMetadataValue function',1);
if (typeof widget.metadata == 'undefined') {
widget.metadata = {};
}
widget.metadata[name] = value;
dashboard.log('dashboardStateChange event thrown for widget ' + widget.id,2);
dashboard.element.trigger("dashboardStateChange",{"stateChange":"metadataChanged","widget":widget});
};
widget.openMenu = function() {
dashboard.log('entering openMenu function',1);
widget.element.find('.' + opts.menuClass).show();
};
widget.closeMenu = function() {
dashboard.log('entering closeMenu function',1);
widget.element.find('.' + opts.menuClass).hide();
};
widget.remove = function() {
dashboard.log('entering remove function',1);
widget.element.remove();
dashboard.log('widgetDeleted event thrown for widget ' + widget.id,2);
widget.element.trigger('widgetDeleted', {"widget":widget});
dashboard.log('dashboardStateChange event thrown for widget ' + widget.id,2);
dashboard.element.trigger("dashboardStateChange",{"stateChange":"widgetRemoved","widget":widget});
};
widget.serialize = function() {
dashboard.log('entering serialize function',1);
var r = '{"title" : "' + widget.title + '", "id" : "' + widget.id + '", "column" : "' + widget.column + '","editurl" : "' + widget.editurl + '","open" : ' + widget.open + ',"url" : "' + widget.url + '"';
if (typeof widget.metadata != 'undefined') {
r+= ',"metadata":{'
var obj = widget.metadata;
var i=0;
for(var item in obj) {
if (i > 0) { r+= ',' };
// FIXME: support for more than string, eg numbers subobjects
r+= '"' + item + '":"' + obj[item] + '"';
i++;
}
r+= '}'
}
r += '}';
return r;
};
widget.openFullscreen = function() {
dashboard.log('entering openFullscreen function',1);
widget.fullscreen = true;
// create a clone
var clone = widget.element.clone();
// move the dashboard
var temp = $('<div style="display:none" id="tempdashboard_' + dashboard.id + '"></div>');
temp.appendTo($("body"));
dashboard.element.children().appendTo(temp);
// add the clone to the dashboard
var fs = $('<ul id="fullscreen_' + dashboard.id + '"></ul>');
fs.appendTo(dashboard.element);
clone.appendTo(fs);
};
widget.closeFullscreen = function() {
dashboard.log('entering closeFullscreen function',1);
widget.fullscreen = false;
// remove the fullscreen
$('#fullscreen_' + dashboard.id).remove();
// move the content from the tempdashboard back
$('#tempdashboard_' + dashboard.id).children().appendTo(dashboard.element);
$('#tempdashboard_' + dashboard.id).remove();
};
widget.openSettings = function() {
dashboard.log('entering openSettings function',1);
widget.element.find('.' + opts.widgetContentClass).load(widget.editurl);
};
// called when widget is initialized
if (widget.open) {
widget.openContent();
}
widget.initialized = true;
dashboard.log('widgetInitialized event thrown',2);
widget.element.trigger("widgetInitialized", {"widget":widget});
return widget;
};
// FIXME: can this be done easier??
function getLayout(id) {
dashboard.log('entering getLayout function',1);
var r = null;
var first = null;
if (typeof opts.layouts != 'undefined') {
$.each(opts.layouts,function(i, item) {
if (i == 0) { first = item; }
if (item.id == id) {
r = item;
}
});
}
if (r == null) { r = first }
return r;
}
$('#' + dashboard.id + ' .menutrigger').live('click', function() {
dashboard.log('widgetOpenMenu event thrown for widget ' + widget.id,2);
var wi = dashboard.getWidget($(this).closest('.' + opts.widgetClass).attr("id"));
wi.element.trigger('widgetOpenMenu', {"widget":wi});
return false;
});
// add event handlers to the menu
$('#' + dashboard.id + ' .' + opts.widgetFullScreenClass).live('click',function(e) {
// close the menu
dashboard.log('widgetCloseMenu event thrown for widget ' + widget.id,2);
var wi = dashboard.getWidget($(this).closest('.' + opts.widgetClass).attr("id"));
wi.element.trigger('widgetCloseMenu', {"widget":wi});
if (wi.fullscreen) {
dashboard.log('widgetCloseFullScreen event thrown for widget ' + wi.id,2);
wi.element.trigger('widgetCloseFullScreen', {"widget":wi});
} else {
dashboard.log('widgetOpenFullScreen event thrown for widget ' + wi.id,2);
wi.element.trigger('widgetOpenFullScreen', {"widget":wi});
}
return false;
});
$('#' + dashboard.id + ' .controls li').live('click',function(e) {
// close the menu
dashboard.log('widgetCloseMenu event thrown for widget ' + widget.id,2);
var wi = dashboard.getWidget($(this).closest('.' + opts.widgetClass).attr("id"));
wi.element.trigger('widgetCloseMenu', {"widget":wi});
// use the class on the li to determine what action to trigger
dashboard.log($(this).attr('class') + ' event thrown for widget ' + widget.id,2);
var wi = dashboard.getWidget($(this).closest('.' + opts.widgetClass).attr("id"));
wi.element.trigger($(this).attr('class'), {"widget":wi});
return false;
});
// add the menu events (by default triggers are connected in dashboard_jsonfeed)
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetCloseMenu',function(e,o) {
dashboard.log("Closing menu " + $(this).attr("id"),1);
o.widget.closeMenu();
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetOpenMenu',function(e,o) {
dashboard.log("Opening menu " + $(this).attr("id"),1);
o.widget.openMenu();
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetDelete',function(e,o) {
if (confirm(opts.deleteConfirmMessage)) {
dashboard.log("Removing widget " + $(this).attr("id"),1);
o.widget.remove();
}
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetRefresh',function(e,o) {
o.widget.refreshContent();
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetSetTitle',function(event, o) {
o.widget.setTitle(o.title);
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetClose',function(e,o) {
dashboard.log("Closing widget " + $(this).attr("id"),1);
o.widget.closeContent();
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetOpen',function(e,o) {
dashboard.log("Opening widget " + $(this).attr("id"),1);
o.widget.openContent();
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetShow',function() {
$(this).find('.' + opts.widgetContentClass).show();
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetHide',function() {
$(this).find('.' + opts.widgetContentClass).hide();
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetAddMetadataValue',function(e,o) {
dashboard.log("Changing metadata for widget " + $(this).attr("id") + ", metadata name: " + o.name + ", value: " + o.value, 1);
o.widget.addMetadataValue(o.name, o.value);
});
// Define a toggle event when clicking at the header
$('#' + dashboard.id + ' .' + opts.widgetTitleClass).live('click',function(e) {
dashboard.log("Click on the header detected for widget " + $(this).attr("id"),1);
if (!$(this).hasClass('noclick')) {
var wi = dashboard.getWidget($(this).closest('.' + opts.widgetClass).attr("id"));
if (wi.open) {
dashboard.log('widgetClose event thrown for widget ' + wi,2);
wi.element.trigger('widgetClose', {"widget":wi});
} else {
dashboard.log('widgetOpen event thrown for widget ' + wi,2);
wi.element.trigger('widgetOpen', {"widget":wi});
}
}
return false;
});
$('#' + dashboard.id + ' .' + opts.widgetHeaderClass).live('mouseover',function () {
$(this).find('.' + opts.iconsClass).removeClass("hidden");
});
$('#' + dashboard.id + ' .' + opts.widgetHeaderClass).live('mouseout', function () {
$(this).find('.' + opts.iconsClass).addClass("hidden");
});
$('body').click(function() {
$('.' + opts.menuClass).hide();
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetOpenFullScreen',function(e,o) {
o.widget.openFullscreen();
});
$('.' + opts.widgetClass).live('widgetCloseFullScreen',function(e,o) {
o.widget.closeFullscreen();
});
$('#' + dashboard.id + ' .' + opts.widgetClass).live('widgetEdit',function(e,o) {
o.widget.openSettings();
});
if ($('#' + addOpts.dialogId).length == 0) dashboard.log('Unable to find ' + addOpts.dialogId,5);
$('#' + addOpts.dialogId).dialog({
autoOpen: false,
height: 414,
width: 550,
modal: true,
buttons: {
Cancel: function() {
$(this).dialog('close');
}
},
close: function() {
//close
}
});
if ($('#' + layoutOpts.dialogId).length == 0) dashboard.log('Unable to find ' + layoutOpts.dialogId,5);
$('#' + layoutOpts.dialogId).dialog({
autoOpen: false,
height: 300,
width: 600,
modal: true
});
$('.' + layoutOpts.openDialogClass).live('click', function(){
dashboard.log('dashboardOpenLayoutDialog event thrown',2);
dashboard.element.trigger("dashboardOpenLayoutDialog");
return false;
});
dashboard.element.live('dashboardOpenLayoutDialog', function(){
dashboard.log('Opening dialog ' + layoutOpts.dialogId,1);
$('#' + layoutOpts.dialogId).dialog('open');
// add the layout images
var h = $('#' + layoutOpts.dialogId).find('.' + layoutOpts.layoutClass);
h.empty();
if (h.children().length == 0) {
dashboard.log('Number of layouts : ' + opts.layouts.length,1);
$.each(opts.layouts,function(i, item) {
dashboard.log('Applying template : ' + layoutOpts.layoutTemplate,1);
if ($('#' + layoutOpts.layoutTemplate).length == 0) dashboard.log('Template "' + layoutOpts.layoutTemplate + ' not found',5);
h.append(tmpl($('#' + layoutOpts.layoutTemplate).html(), item));
});
}
// set the selected class for the selected layout
$('.' + layoutOpts.selectLayoutClass).removeClass(layoutOpts.selectedLayoutClass);
$('#' + dashboard.layout.id).addClass(layoutOpts.selectedLayoutClass);
bindSelectLayout();
});
dashboard.element.live('dashboardStateChange', function(){
if (typeof opts.stateChangeUrl != 'undefined' && opts.stateChangeUrl != null && opts.stateChangeUrl != '') {
$.ajax({type: 'POST',
url: opts.stateChangeUrl,
data: { dashboard: dashboard.element.attr("id"), settings: dashboard.serialize() },
success: function(data){
if (data == "NOK" || data.indexOf('<response>NOK</response>') != -1){
dashboard.log('dashboardSaveFailed event thrown',2);
dashboard.element.trigger("dashboardSaveFailed");
} else {
dashboard.log('dashboardSuccessfulSaved event thrown',2);
dashboard.element.trigger("dashboardSuccessfulSaved");
}
},
error: function(XMLHttpRequest, textStatus, errorThrown){
dashboard.log('dashboardSaveFailed event thrown',2);
dashboard.element.trigger("dashboardSaveFailed");
},
dataType: "text"
});
}
});
dashboard.element.live('dashboardCloseLayoutDialog', function(){
// close the dialog
$('#' + layoutOpts.dialogId).dialog('close');
});
// FIXME: why doesn't the live construct work in this case
function bindSelectLayout() {
if ($('.' + layoutOpts.selectLayoutClass).length == 0) dashboard.log('Unable to find ' + layoutOpts.selectLayoutClass,5);
$('.' + layoutOpts.selectLayoutClass).bind('click', function(e){
var currentLayout = dashboard.layout;
dashboard.log('dashboardCloseLayoutDialog event thrown',2);
dashboard.element.trigger('dashboardCloseLayoutDialog');
// Now set the new layout
var newLayout = getLayout($(this).attr("id"));
dashboard.layout = newLayout;
// remove the class of the old layout
if (typeof opts.layoutClass != 'undefined') {
dashboard.element.find('.' + opts.layoutClass).removeClass(currentLayout.classname).addClass(newLayout.classname);
fixSortableColumns();
// check if there are widgets in hidden columns, move them to the first column
if ($('.' + opts.columnClass).length == 0) dashboard.log('Unable to find ' + opts.columnClass,5);
dashboard.element.find('.' + opts.columnClass).each(function() {
if ($(this).css("display") == "none") {
// move the widgets to the first column
$(this).children().appendTo(dashboard.element.find('.' + opts.columnClass + ':first'));
}
$('.emptycolumn').remove();
// add the text to the empty columns
$('.' + opts.columnClass).each(function() {
if ($(this).children().length == 0) {
$(this).html('<div class="emptycolumn">' + opts.emptyColumnHtml + '</div>');
}
});
});
} else {
// set the new layout, but first move the dashboard to a temp
var temp = $('<div style="display:none" id="tempdashboard"></div>');
temp.appendTo($("body"));
dashboard.element.children().appendTo(temp);
// reload the dashboard
dashboard.init();
}
// throw an event upon changing the layout.
dashboard.log('dashboardChangeLayout event thrown',2);
dashboard.element.trigger('dashboardLayoutChanged');
});
return false;
}
$('.' + addOpts.selectCategoryClass).live('click', function(){
dashboard.log('addWidgetDialogSelectCategory event thrown',2);
dashboard.element.trigger('addWidgetDialogSelectCategory', {"category":$(this)});
return false;
});
dashboard.element.live('addWidgetDialogSelectCategory', function(e, obj){
// remove the category selection
$('.' + addOpts.selectCategoryClass).removeClass(addOpts.selectedCategoryClass);
// empty the widgets div
$('#' + addOpts.dialogId).find('.' + addOpts.widgetClass).empty();
// select the category
$(obj.category).addClass(addOpts.selectedCategoryClass);
// get the widgets
url = dashboard.widgetCategories[$(obj.category).attr("id")];
dashboard.log('Getting JSON feed : ' + url,1);
$.getJSON(url, {"cache":true}, function(json) {
// load the widgets from the category
if (json.data == 0) dashboard.log('Empty data returned',3);
$.each(json.data, function(i,item){
dashboard.widgetsToAdd[item.id] = item;
dashboard.log('Applying template : ' + addOpts.widgetTemplate,1);
if ($('#' + addOpts.widgetTemplate).length == 0) dashboard.log('Template "' + addOpts.widgetTemplate + ' not found',5);
var html = tmpl($('#' + addOpts.widgetTemplate).html(), item);
$('#' + addOpts.dialogId).find('.' + addOpts.widgetClass).append(html);
});
});
dashboard.log('addWidgetDialogWidgetsLoaded event thrown',2);
dashboard.element.trigger('addWidgetDialogWidgetsLoaded');
});
$('.' + addOpts.addWidgetClass).live('click', function(){
var widget = dashboard.widgetsToAdd[$(this).attr("id").replace('addwidget','')];
dashboard.log('dashboardAddWidget event thrown',2);
dashboard.element.trigger('dashboardAddWidget', {"widget":widget});
dashboard.log('dashboardCloseWidgetDialog event thrown',2);
dashboard.element.trigger('dashboardCloseWidgetDialog');
return false;
});
$('.' + addOpts.openDialogClass).live('click', function(){
dashboard.log('dashboardOpenWidgetDialog event thrown',2);
dashboard.element.trigger('dashboardOpenWidgetDialog');
return false;
});
dashboard.element.live('dashboardCloseWidgetDialog', function(){
// close the dialog
$('#' + addOpts.dialogId).dialog('close');
});
dashboard.element.live('dashboardOpenWidgetDialog', function(){
//remove existing categories/widgets from the DOM, to prevent duplications
$('#' + addOpts.dialogId).find('.' + addOpts.categoryClass).empty();
$('#' + addOpts.dialogId).find('.' + addOpts.widgetClass).empty();
dashboard.log('Opening dialog ' + addOpts.dialogId,1);
$('#' + addOpts.dialogId).dialog('open');
dashboard.log('Getting JSON feed : ' + addOpts.widgetDirectoryUrl,1);
$.getJSON(addOpts.widgetDirectoryUrl, function(json) {
if (json.category == 0) dashboard.log('Empty data returned',3);
$.each(json.category, function(i,item){
// Add the categories to the dashboard
dashboard.widgetCategories[item.id] = item.url;
dashboard.log('Applying template : ' + addOpts.categoryTemplate,1);
if ($('#' + addOpts.categoryTemplate).length == 0) dashboard.log('Template "' + addOpts.categoryTemplate + ' not found',5);
var html = tmpl($('#' + addOpts.categoryTemplate).html(),item);
$('#' + addOpts.dialogId).find('.' + addOpts.categoryClass).append(html);
});
dashboard.log('addWidgetDialogCategoriesLoaded event thrown',2);
dashboard.element.trigger('addWidgetDialogCategoriesLoaded');
dashboard.log('addWidgetDialogSelectCategory event thrown',2);
dashboard.element.trigger('addWidgetDialogSelectCategory', {"category":$('#' + addOpts.dialogId).find('.' + addOpts.categoryClass + '>li:first')});
});
});
return dashboard;
};
// Public static properties of dashboard. Default settings.
$.fn.dashboard.defaults = {
debuglevel:3,
json_data: {},
loadingHtml: '<div class="loading"><img alt="Loading, please wait" src="../themes/default/loading.gif" /><p>Loading...</p></div>',
emptyColumnHtml: 'Drag your widgets here',
widgetTemplate: 'widgettemplate',
columnPrefix: 'column-',
opacity:"0.2",
deleteConfirmMessage: "Are you sure you want to delete this widget?",
widgetNotFoundHtml: "The content of this widget is not available anymore. You may remove this widget.",
columnClass: 'column',
widgetClass: 'widget',
menuClass: 'controls',
widgetContentClass: 'widgetcontent',
widgetTitleClass: 'widgettitle',
widgetHeaderClass: 'widgetheader',
widgetFullScreenClass: 'widgetopenfullscreen',
iconsClass: 'icons',
stateChangeUrl: '',
addWidgetSettings: {
openDialogClass: 'openaddwidgetdialog',
addWidgetClass: 'addwidget',
selectCategoryClass: 'selectcategory',
selectedCategoryClass: 'selected',
categoryClass: 'categories',
widgetClass: 'widgets',
dialogId: 'addwidgetdialog',
categoryTemplate: 'categorytemplate',
widgetTemplate: 'addwidgettemplate'
},
editLayoutSettings: {
dialogId: 'editLayout',
layoutClass: 'layoutselection',
selectLayoutClass: 'layoutchoice',
selectedLayoutClass: 'selected',
openDialogClass: 'editlayout',
layoutTemplate: 'selectlayouttemplate'
}
};
// Default widget settings.
$.fn.dashboard.widget = {
defaults: {
open: true,
fullscreen: false,
loaded: false,
url: '',
metadata: {}
}
};
})(jQuery); // end of closure
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
var cache = {};
this.tmpl = function tmpl(str, data){
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
tmpl(document.getElementById(str).innerHTML) :
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
// Provide some basic currying to the user
return data ? fn( data ) : fn;
};
})();

View File

@ -1,85 +0,0 @@
<script type="text/html" id="categorytemplate">
<li id="<%= id %>" class="selectcategory"><button><%= title %> (<%= amount %>)</button></li>
</script>
<script type="text/html" id="widgettemplate">
<div class="ui-widget ui-corner-all ui-widget-content widget" id="<%= id %>" title="<%= title %>">
<div class="ui-widget-header ui-corner-all widgetheader">
<span class="widgettitle"><%= title %></span>
<span class="right icons hidden">
<span class="ui-icon ui-icon-newwin widgetopenfullscreen"></span>
<span class="ui-icon ui-icon-arrowthickstop-1-s menutrigger"></span>
<span class="hiddenmenu">
<ul style="top: 13px;" class="hidden controls ui-widget-header">
<li class="widgetClose">
<span class="ui-icon ui-icon-minus"></span>
<a class="minimization" href="#">Minimize</a>
</li>
<li class="widgetOpen">
<span class="ui-icon ui-icon-extlink"></span>
<a class="minimization" href="#">Maximize</a>
</li>
<li class="widgetDelete">
<span class="ui-icon ui-icon-close"></span>
<a class="delete" href="#">Delete</a>
</li>
<!-- This could be implemented -->
<!--
<li class="widgetEdit">
<span class="ui-icon ui-icon-tag"></span>
<a class="no_target" href="#">Edit</a>
</li>
-->
<li class="widgetRefresh">
<span class="ui-icon ui-icon-arrowrefresh-1-w"></span>
<a class="no_target" href="#">Refresh</a>
</li>
</ul>
</span>
</span>
</div>
<div class="widgetcontent">
</div>
</div>
</script>
<script type="text/html" id="selectlayouttemplate">
<li class="layoutchoice" id="<%= id %>" style="background-image: url('<%= image %>')"></li>
</script>
<script type="text/html" id="addwidgettemplate">
<li class="widgetitem">
<img src="<%= image %>" alt="" height="60" width="120">
<div class="add-button">
<input class="macro-button-add addwidget" id="addwidget<%= id %>" value="Add it Now" type="button"><br>
<input class="macro-hidden-uri" value="<%= url %>" type="hidden">
</div>
<!-- // .add-button -->
<h3><a href=""><%= title %></a></h3>
<p>By <%= creator %></p>
<p><%= description %></p>
</li>
</script>
<div class="dialog" id="addwidgetdialog" title="Widget Directory">
<ul class="categories">
</ul>
<div class="panel-body">
<ol id="category-all" class="widgets">
</ol>
</div>
</div>
<div class="dialog" id="editLayout" title="Edit layout">
<div class="panel-body" id="layout-dialog">
<p><strong>Choose dashboard layout</strong></p>
<ul class="layoutselection">
</ul>
</div>
</div>

View File

@ -10,34 +10,108 @@ openerp.base.form.Board = openerp.base.form.Widget.extend({
},
start: function() {
this._super.apply(this, arguments);
this.$element.html(QWeb.render(this.template));
},
render: function() {
var self = this;
jQuery('body').append(
jQuery('<div>', {'id': 'dashboard_template'}).load('/base_dashboard/static/src/dashboard_template.html',self.on_loaded).hide()
)
},
on_loaded: function() {
this.$element.html(QWeb.render(this.template));
var $dashboard = this.$element.find('#dashboard');
var children = this.node.children;
var board = jQuery('#dashboard').dashboard({
layoutClass:'layout'
});
board.init();
for(var ch = 0; ch < children.length; ch++) {
var ch_widgets = children[ch].children;
for(var chld = 0; chld < ch_widgets.length; chld++) {
var widget_type = ch_widgets[chld].tag;
var child_index = widget_type == 'action' ? chld : ch;
var widget = new (openerp.base.form.widgets.get_object(widget_type)) (this.view, ch_widgets[chld], board, child_index);
widget.start();
for(var ch=0; ch < children.length; ch++) {
var node = children[ch];
var widget;
if(node.tag.indexOf('child') >= 0) {
widget = new (openerp.base.form.widgets.get_object('child')) (this.view, node, $dashboard);
} else {
//Vpaned
widget = new (openerp.base.form.widgets.get_object(node.tag)) (this.view, node, $dashboard);
}
widget.start();
}
}
jQuery('.column').css('width', 100/children.length+'%');
},
// render: function() {
// var self = this;
// var children = this.node.children;
// console.log('test>>>',this.$element)
//// jQuery('body').append(
//// jQuery('<div>', {'id': 'dashboard_template'}).load('/base_dashboard/static/src/dashboard_template.html',self.on_loaded).hide()
//// )
// },
// on_loaded: function() {
// var children = this.node.children;
// var board = jQuery('#dashboard').dashboard({
// layoutClass:'layout'
// });
// board.init();
// for(var ch = 0; ch < children.length; ch++) {
// var ch_widgets = children[ch].children;
// for(var chld = 0; chld < ch_widgets.length; chld++) {
// var widget_type = ch_widgets[chld].tag;
// var child_index = widget_type == 'action' ? chld : ch;
// var widget = new (openerp.base.form.widgets.get_object(widget_type)) (this.view, ch_widgets[chld], board, child_index);
// widget.start();
// }
// }
// }
});
openerp.base.form.Dashbar = openerp.base.form.Widget.extend({
init: function(view, node, dashboard) {
this._super(view, node, dashboard);
this.dashboard = dashboard;
},
start: function() {
var $dashboard = this.dashboard;
var children = this.node.children;
$dashboard.append(
jQuery('<div>',{
'class': 'column',
'id': this.node.tag
})
)
for(var chld=0; chld < children.length;chld++) {
var child = children[chld];
$dashboard.find('#'+this.node.tag).append(
jQuery('<div>',{
'class': 'portlet'
}
).append(
jQuery('<div>',{
'class': 'portlet-header'
}).html(child.attrs.string),
jQuery('<div>',{
'class': 'portlet-content'
}).html('test')
)
)
}
$( ".column" ).sortable({
connectWith: ".column"
});
$( ".portlet" ).addClass( "ui-widget ui-widget-content ui-helper-clearfix ui-corner-all" )
.find( ".portlet-header" )
.addClass( "ui-widget-header ui-corner-all" )
.prepend( "<span class='ui-icon ui-icon-minusthick'></span>")
.end()
.find( ".portlet-content" );
$( ".portlet-header .ui-icon" ).click(function() {
$( this ).toggleClass( "ui-icon-minusthick" ).toggleClass( "ui-icon-plusthick" );
$( this ).parents( ".portlet:first" ).find( ".portlet-content" ).toggle();
});
$( ".column" ).disableSelection();
},
})
openerp.base.form.Action = openerp.base.form.Widget.extend({
init: function(view, node, board, child_index) {
this._super(view, node, board, child_index);
@ -99,6 +173,7 @@ openerp.base.form.Vpaned = openerp.base.form.Widget.extend({
})
openerp.base.form.widgets.add('hpaned', 'openerp.base.form.Board');
openerp.base.form.widgets.add('child', 'openerp.base.form.Dashbar');
openerp.base.form.widgets.add('vpaned', 'openerp.base.form.Vpaned');
openerp.base.form.widgets.add('action', 'openerp.base.form.Action');
}

View File

@ -1,12 +1,15 @@
<template>
<t t-name="Board">
<!--
<div id="dashboard" class="dashboard">
<!-- this HTML covers all layouts. The 5 different layouts are handled by setting another layout classname -->
this HTML covers all layouts. The 5 different layouts are handled by setting another layout classname
<div class="layout">
<div id="column-first" class="column first column-first"></div>
<div id="column-second" class="column second column-second"></div>
<div id="column-third" class="column third column-third"></div>
</div>
</div>-->
<div id="dashboard" class="demo">
</div>
</t>
</template>