diff --git a/.gitignore b/.gitignore index 1c6dbb5761..e158c010fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pyc *.comp.js *.DS_Store +user_files defs.py diff --git a/cgi-bin/core/doctype/doctype_mapper/doctype_mapper.py b/cgi-bin/core/doctype/doctype_mapper/doctype_mapper.py index 7fb0f61766..b9b3382ec0 100644 --- a/cgi-bin/core/doctype/doctype_mapper/doctype_mapper.py +++ b/cgi-bin/core/doctype/doctype_mapper/doctype_mapper.py @@ -18,230 +18,248 @@ convert_to_lists = webnotes.conn.convert_to_lists class DocType: - def __init__(self, doc, doclist=[]): - self.doc = doc - self.doclist = doclist - self.prefix = is_testing and 'test' or 'tab' - self.ref_doc = '' - # Autoname - #--------- - def autoname(self): - self.doc.name = make_autoname(self.doc.from_doctype + '-' + self.doc.to_doctype) + def __init__(self, doc, doclist=[]): + self.doc = doc + self.doclist = doclist + self.prefix = is_testing and 'test' or 'tab' + self.ref_doc = '' + # Autoname + #--------- + def autoname(self): + self.doc.name = make_autoname(self.doc.from_doctype + '-' + self.doc.to_doctype) - # Map Custom Fields - # ------------------ - def map_custom_fields(self, from_doctype, to_doctype, from_doc, to_doc): - fld_list = [] - for d in sql("select fieldname from `tabCustom Field` where dt = %s and docstatus != 2",from_doctype): - if sql("select fieldname from `tabCustom Field` where dt = %s and fieldname = %s and docstatus != 2",(to_doctype, d[0])): - fld_list.append([d[0], d[0]]) - self.set_value(fld_list, from_doc, to_doc) + + def map_fields_with_same_name(self, from_doctype, to_doctype, from_doc, to_doc, fld_list): + """ + Returns field list with same name in from and to doctype + """ + exception_flds = [f[0] for f in fld_list if f[2] == 'No'] + + map_fld_list = [ + [d[0], d[0], 'Yes'] for d in sql(""" + select t1.fieldname + from `tabDocField` t1, `tabDocField` t2 + where t1.parent = %s and t2.parent = %s + and t1.fieldname = t2.fieldname + and t1.docstatus != 2 and t2.docstatus != 2 + and ifnull(t1.fieldname, '') != '' + """,(from_doctype, to_doctype)) if d[0] not in exception_flds + ] + + self.set_value(map_fld_list, from_doc, to_doc) + + + # Maps the fields in 'To DocType' + #-------------------------------- + def dt_map(self, from_doctype, to_doctype, from_docname, to_doc, doclist, from_to_list = '[]'): - # Maps the fields in 'To DocType' - #-------------------------------- - def dt_map(self, from_doctype, to_doctype, from_docname, to_doc, doclist, from_to_list = '[]'): + # definition of arguments + ''' + String : contains the name of DocType initiating the function + String : contains the name of DocType created by the function + String : contains ID(name) of 'from_doctype' + String : contains doc of 'to_doctype' + String : contains doclist of 'to_doctype' + String : contains list of tables which will be mapped + ''' + # Validate reference doc docstatus + self.ref_doc = from_docname + self.check_ref_docstatus() + + if not doclist: + doclist.append(to_doc) - # definition of arguments - ''' - String : contains the name of DocType initiating the function - String : contains the name of DocType created by the function - String : contains ID(name) of 'from_doctype' - String : contains doc of 'to_doctype' - String : contains doclist of 'to_doctype' - String : contains list of tables which will be mapped - ''' - # Validate reference doc docstatus - self.ref_doc = from_docname - self.check_ref_docstatus() - - if not doclist: - doclist.append(to_doc) + tbl_list = sql("select from_table, to_table, from_field, to_field, match_id, validation_logic from `tabTable Mapper Detail` where parent ='%s' order by match_id" % (from_doctype + "-" + to_doctype)) - tbl_list = sql("select from_table, to_table, from_field, to_field, match_id, validation_logic from `tabTable Mapper Detail` where parent ='%s' order by match_id" % (from_doctype + "-" + to_doctype)) + for t in tbl_list: + from_table_name = t[0] + to_table_name = t[1] + from_table_fname = t[2] + to_table_fname = t[3] + match_id = t[4] + validation_logic = t[5] + - for t in tbl_list: - from_table_name = t[0] - to_table_name = t[1] - from_table_fname = t[2] - to_table_fname = t[3] - match_id = t[4] - validation_logic = t[5] - + from_to = [from_table_name, to_table_name] - from_to = [from_table_name, to_table_name] + if from_to in eval(from_to_list): + fld_list = sql("select from_field, to_field, map from `tabField Mapper Detail` where parent = '%s' and match_id = %s" % (from_doctype + "-" + to_doctype, match_id)) + if fld_list: + if not from_docname: + msgprint(from_doctype + " not selected for mapping") + raise Exception + + # Parent to parent mapping + if from_table_name == self.doc.from_doctype and to_table_name == self.doc.to_doctype: + + # Check validation + nm = sql("select name from `tab%s` where name = '%s' and %s" % (from_doctype, from_docname, validation_logic)) + nm = nm and nm[0][0] or '' + + # If validation failed raise exception + if not nm: + msgprint("Validation failed in doctype mapper. Please contact Administrator.") + raise Exception + + from_doc = Document(from_doctype, nm) - if from_to in eval(from_to_list): - fld_list = sql("select from_field, to_field from `tabField Mapper Detail` where parent = '%s' and match_id = %s and map = 'Yes'" % (from_doctype + "-" + to_doctype, match_id)) - if fld_list: - if not from_docname: - msgprint(from_doctype + " not selected for mapping") - raise Exception - - # Parent to parent mapping - if from_table_name == self.doc.from_doctype and to_table_name == self.doc.to_doctype: - - # Check validation - nm = sql("select name from `tab%s` where name = '%s' and %s" % (from_doctype, from_docname, validation_logic)) - nm = nm and nm[0][0] or '' - - # If validation failed raise exception - if not nm: - msgprint("Validation failed in doctype mapper. Please contact Administrator.") - raise Exception - - from_doc = Document(from_doctype, nm) - # Maps field in parent - self.set_value(fld_list, from_doc, to_doc) - # Map custom fields - self.map_custom_fields(from_doctype, to_doctype, from_doc, to_doc) + # Map fields with same name + self.map_fields_with_same_name(from_doctype, to_doctype, from_doc, to_doc, fld_list) + # Maps field in parent + self.set_value(fld_list, from_doc, to_doc) - # Parent to child OR child to child mapping - else: - dnlist = () - if from_table_name == self.doc.from_doctype: - dnlist = ((from_docname,),) - else: - dnlist = sql("select name from `tab%s` where parent='%s' and parenttype = '%s' and %s order by idx" % (from_table_name, from_docname, self.doc.from_doctype, validation_logic)) - - for dn in dnlist: - # Add a row in target table in 'To DocType' and returns obj - ch = addchild(to_doc, t[3], t[1], 1, doclist) - # Creates object for 'From DocType', it can be parent or child - d = Document(t[0], dn[0]) - # Map values - self.set_value(fld_list, d, ch) - # Map custom fields - self.map_custom_fields(from_table_name, t[1], d, ch) - # Required when called from server side for refreshing table - return doclist - - # Assigns value to "To Doctype" - #------------------------------ - def set_value(self, fld_list, obj, to_doc): - for f in fld_list: - if f[0].startswith('eval:'): - to_doc.fields[f[1]] = eval(f[0][5:]) - else: - to_doc.fields[f[1]] = obj.fields.get(f[0]) - - # Validate - #--------- - def validate(self): - for d in getlist(self.doclist, 'field_mapper_details'): - # Automatically assigns default value if not entered - if not d.match_id: - d.match_id = 0 - if not d.map: - d.map = 'Yes' - for d in getlist(self.doclist, 'table_mapper_details'): - if not d.reference_doctype_key: - d.reference_doctype_key = '' - if not d.reference_key: - d.reference_key = '' - - # Check wrong field name - self.check_fields_in_dt() + # Parent to child OR child to child mapping + else: + dnlist = () + if from_table_name == self.doc.from_doctype: + dnlist = ((from_docname,),) + else: + dnlist = sql("select name from `tab%s` where parent='%s' and parenttype = '%s' and %s order by idx" % (from_table_name, from_docname, self.doc.from_doctype, validation_logic)) + + for dn in dnlist: + # Add a row in target table in 'To DocType' and returns obj + ch = addchild(to_doc, t[3], t[1], 1, doclist) + # Creates object for 'From DocType', it can be parent or child + d = Document(t[0], dn[0]) + # Map fields with same name + self.map_fields_with_same_name(from_table_name, t[1], d, ch, fld_list) + # Map values + self.set_value(fld_list, d, ch) - # Check if any wrong fieldname entered - #-------------------------------------- - def check_fields_in_dt(self): - for d in getlist(self.doclist, 'field_mapper_details'): - table_name = sql("select from_table, to_table from `tabTable Mapper Detail` where parent ='%s' and match_id = '%s'" % (self.doc.name, d.match_id)) - - if table_name: - exists1 = sql("select name from tabDocField where parent = '%s' and fieldname = '%s'" % (table_name[0][0], d.from_field)) - exists2 = sql("select name from tabDocField where parent = '%s' and fieldname = '%s'" % (table_name[0][1], d.to_field)) - - # Default fields like name, parent, owner does not exists in DocField - if not exists1 and d.from_field not in default_fields: - msgprint('"' + cstr(d.from_field) + '" does not exists in DocType "' + cstr(table_name[0][0]) + '"') - if not exists2 and d.to_field not in default_fields: - msgprint('"' + cstr(d.to_field) + '" does not exists in DocType "' + cstr(table_name[0][1]) + '"') - - # Check consistency of value with reference document - #--------------------------------------------------- - def validate_reference_value(self, obj, to_docname): - for t in getlist(self.doclist, 'table_mapper_details'): - # Reference key is the fieldname which will relate to the from_table - if t.reference_doctype_key: - for d in getlist(obj.doclist, t.to_field): - if d.fields[t.reference_doctype_key] == self.doc.from_doctype: - self.check_consistency(obj.doc, d, to_docname) - self.check_ref_docstatus() - - # Make list of fields whose value will be consistent with prevdoc - #----------------------------------------------------------------- - def get_checklist(self): - checklist = [] - for f in getlist(self.doclist, 'field_mapper_details'): - - # Check which field's value will be compared - if f.checking_operator: - checklist.append([f.from_field, f.to_field, f.checking_operator, f.match_id]) - return checklist - - def check_fld_type(self, tbl, fld, cur_val): - ft = sql("select fieldtype from tabDocField where fieldname = '%s' and parent = '%s'" % (fld,tbl)) - ft = ft and ft[0][0] or '' - if ft == 'Currency' or ft == 'Float': - cur_val = '%.2f' % cur_val - return cur_val, ft - - # Check consistency - #------------------- - def check_consistency(self, par_obj, child_obj, to_docname): - checklist = self.get_checklist() - self.ref_doc = '' - for t in getlist(self.doclist, 'table_mapper_details'): - if t.reference_key and child_obj.fields[t.reference_key]: - for cl in checklist: - if cl[3] == t.match_id: - if t.to_field: - cur_val = child_obj.fields[cl[1]] - else: - cur_val = par_obj.fields[cl[1]] - - ft = self.check_fld_type(t.to_table, cl[1], cur_val) - cur_val = ft[0] - if cl[2] == '=' and (ft[1] == 'Currency' or ft[1] == 'Float'): - consistent = sql("select name, %s from `tab%s` where name = '%s' and '%s' - %s <= 0.5" % (cl[0], t.from_table, child_obj.fields[t.reference_key], flt(cur_val), cl[0])) - else: - #consistent = sql("select name, %s from `tab%s` where name = '%s' and '%s' %s %s" % (cl[0], t.from_table, child_obj.fields[t.reference_key], cur_val, cl[2], cl[0])) - consistent = sql("select name, %s from `tab%s` where name = '%s' and '%s' %s ifnull(%s, '')" % (cl[0], t.from_table, child_obj.fields[t.reference_key], ft[1] in ('Currency', 'Float', 'Int') and flt(cur_val) or cstr(cur_val), cl[2], cl[0])) - if not self.ref_doc: - det = sql("select name, parent from `tab%s` where name = '%s'" % (t.from_table, child_obj.fields[t.reference_key])) - self.ref_doc = det[0][1] and det[0][1] or det[0][0] + # Required when called from server side for refreshing table + return doclist + + # Assigns value to "To Doctype" + #------------------------------ + def set_value(self, fld_list, obj, to_doc): + for f in fld_list: + if f[2] == 'Yes': + if f[0].startswith('eval:'): + to_doc.fields[f[1]] = eval(f[0][5:]) + else: + to_doc.fields[f[1]] = obj.fields.get(f[0]) - if not consistent: - self.give_message(t.from_table, t.to_table, cl[0], cl[1], child_obj.fields[t.reference_key], cl[2]) - - # Gives message and raise exception - #----------------------------------- - def give_message(self, from_table, to_table, from_field, to_field, ref_value, operator): - # Select label of the field - to_fld_label = sql("select label from tabDocField where parent = '%s' and fieldname = '%s'" % (to_table, to_field)) - from_fld_label = sql("select label from tabDocField where parent = '%s' and fieldname = '%s'" % (from_table, from_field)) - - op_in_words = {'=':'equal to ', '>=':'greater than equal to ', '>':'greater than ', '<=':'less than equal to ', '<':'less than '} - msgprint(to_fld_label[0][0] + " should be " + op_in_words[operator] + from_fld_label[0][0] + " of " + self.doc.from_doctype + ": " + self.ref_doc) - raise Exception, "Validation Error." - - def check_ref_docstatus(self): - if self.ref_doc: - det = sql("select name, docstatus from `tab%s` where name = '%s'" % (self.doc.from_doctype, self.ref_doc)) - if not det: - msgprint(self.doc.from_doctype + ": " + self.ref_doc + " does not exists in the system") - raise Exception, "Validation Error." - elif self.doc.ref_doc_submitted and det[0][1] != 1: - msgprint(self.doc.from_doctype + ": " + self.ref_doc + " is not Submitted Document.") - raise Exception, "Validation Error." + + # Validate + #--------- + def validate(self): + for d in getlist(self.doclist, 'field_mapper_details'): + # Automatically assigns default value if not entered + if not d.match_id: + d.match_id = 0 + if not d.map: + d.map = 'Yes' + for d in getlist(self.doclist, 'table_mapper_details'): + if not d.reference_doctype_key: + d.reference_doctype_key = '' + if not d.reference_key: + d.reference_key = '' + + # Check wrong field name + self.check_fields_in_dt() - def on_update(self): - import webnotes.defs - if hasattr(webnotes.defs, 'developer_mode') and webnotes.defs.developer_mode: - from webnotes.modules.export_module import export_to_files - export_to_files(record_list=[[self.doc.doctype, self.doc.name]]) - + # Check if any wrong fieldname entered + #-------------------------------------- + def check_fields_in_dt(self): + for d in getlist(self.doclist, 'field_mapper_details'): + table_name = sql("select from_table, to_table from `tabTable Mapper Detail` where parent ='%s' and match_id = '%s'" % (self.doc.name, d.match_id)) + + if table_name: + exists1 = sql("select name from tabDocField where parent = '%s' and fieldname = '%s'" % (table_name[0][0], d.from_field)) + exists2 = sql("select name from tabDocField where parent = '%s' and fieldname = '%s'" % (table_name[0][1], d.to_field)) + + # Default fields like name, parent, owner does not exists in DocField + if not exists1 and d.from_field not in default_fields: + msgprint('"' + cstr(d.from_field) + '" does not exists in DocType "' + cstr(table_name[0][0]) + '"') + if not exists2 and d.to_field not in default_fields: + msgprint('"' + cstr(d.to_field) + '" does not exists in DocType "' + cstr(table_name[0][1]) + '"') + + # Check consistency of value with reference document + #--------------------------------------------------- + def validate_reference_value(self, obj, to_docname): + for t in getlist(self.doclist, 'table_mapper_details'): + # Reference key is the fieldname which will relate to the from_table + if t.reference_doctype_key: + for d in getlist(obj.doclist, t.to_field): + if d.fields[t.reference_doctype_key] == self.doc.from_doctype: + self.check_consistency(obj.doc, d, to_docname) + self.check_ref_docstatus() + + # Make list of fields whose value will be consistent with prevdoc + #----------------------------------------------------------------- + def get_checklist(self): + checklist = [] + for f in getlist(self.doclist, 'field_mapper_details'): + + # Check which field's value will be compared + if f.checking_operator: + checklist.append([f.from_field, f.to_field, f.checking_operator, f.match_id]) + return checklist + + def check_fld_type(self, tbl, fld, cur_val): + ft = sql("select fieldtype from tabDocField where fieldname = '%s' and parent = '%s'" % (fld,tbl)) + ft = ft and ft[0][0] or '' + if ft == 'Currency' or ft == 'Float': + cur_val = '%.2f' % cur_val + return cur_val, ft + + # Check consistency + #------------------- + def check_consistency(self, par_obj, child_obj, to_docname): + checklist = self.get_checklist() + self.ref_doc = '' + for t in getlist(self.doclist, 'table_mapper_details'): + if t.reference_key and child_obj.fields[t.reference_key]: + for cl in checklist: + if cl[3] == t.match_id: + if t.to_field: + cur_val = child_obj.fields[cl[1]] + else: + cur_val = par_obj.fields[cl[1]] + + ft = self.check_fld_type(t.to_table, cl[1], cur_val) + cur_val = ft[0] + + if cl[2] == '=' and (ft[1] == 'Currency' or ft[1] == 'Float'): + consistent = sql("select name, %s from `tab%s` where name = '%s' and '%s' - %s <= 0.5" % (cl[0], t.from_table, child_obj.fields[t.reference_key], flt(cur_val), cl[0])) + else: + #consistent = sql("select name, %s from `tab%s` where name = '%s' and '%s' %s %s" % (cl[0], t.from_table, child_obj.fields[t.reference_key], cur_val, cl[2], cl[0])) + consistent = sql("select name, %s from `tab%s` where name = '%s' and '%s' %s ifnull(%s, '')" % (cl[0], t.from_table, child_obj.fields[t.reference_key], ft[1] in ('Currency', 'Float', 'Int') and flt(cur_val) or cstr(cur_val), cl[2], cl[0])) + + if not self.ref_doc: + det = sql("select name, parent from `tab%s` where name = '%s'" % (t.from_table, child_obj.fields[t.reference_key])) + self.ref_doc = det[0][1] and det[0][1] or det[0][0] + + if not consistent: + self.give_message(t.from_table, t.to_table, cl[0], cl[1], child_obj.fields[t.reference_key], cl[2]) + + # Gives message and raise exception + #----------------------------------- + def give_message(self, from_table, to_table, from_field, to_field, ref_value, operator): + # Select label of the field + to_fld_label = sql("select label from tabDocField where parent = '%s' and fieldname = '%s'" % (to_table, to_field)) + from_fld_label = sql("select label from tabDocField where parent = '%s' and fieldname = '%s'" % (from_table, from_field)) + + op_in_words = {'=':'equal to ', '>=':'greater than equal to ', '>':'greater than ', '<=':'less than equal to ', '<':'less than '} + msgprint(to_fld_label[0][0] + " should be " + op_in_words[operator] + from_fld_label[0][0] + " of " + self.doc.from_doctype + ": " + self.ref_doc) + raise Exception, "Validation Error." + + def check_ref_docstatus(self): + if self.ref_doc: + det = sql("select name, docstatus from `tab%s` where name = '%s'" % (self.doc.from_doctype, self.ref_doc)) + if not det: + msgprint(self.doc.from_doctype + ": " + self.ref_doc + " does not exists in the system") + raise Exception, "Validation Error." + elif self.doc.ref_doc_submitted and det[0][1] != 1: + msgprint(self.doc.from_doctype + ": " + self.ref_doc + " is not Submitted Document.") + raise Exception, "Validation Error." + + def on_update(self): + import webnotes.defs + if hasattr(webnotes.defs, 'developer_mode') and webnotes.defs.developer_mode: + from webnotes.modules.export_module import export_to_files + export_to_files(record_list=[[self.doc.doctype, self.doc.name]]) + diff --git a/cgi-bin/core/doctype/event/event.js b/cgi-bin/core/doctype/event/event.js new file mode 100644 index 0000000000..dc229bd7b3 --- /dev/null +++ b/cgi-bin/core/doctype/event/event.js @@ -0,0 +1,9 @@ +cur_frm.cscript.onload = function(doc, cdt, cdn) { + var df = get_field('Event', 'Intro HTML', doc.name); + if(doc.ref_type) { + ref = repl(cur_frm.cstring.ref_html, {'dt': doc.ref_type, 'dn':doc.ref_name}); + } else var ref = ''; + + df.options = repl(cur_frm.cstring.intro_html, {'ref': ref}); + refresh_fields('Intro HTML'); +} diff --git a/cgi-bin/core/doctype/file/file.py b/cgi-bin/core/doctype/file/file.py new file mode 100644 index 0000000000..b957ea4c15 --- /dev/null +++ b/cgi-bin/core/doctype/file/file.py @@ -0,0 +1,14 @@ +class DocType: + def __init__(self, d, dl): + self.doc, self.doclist = d,dl + + def validate(self): + # check for extension + if not '.' in self.doc.file_name: + msgprint("Extension required in file name") + raise Exception + + # set mime type + if not self.doc.mime_type: + import mimetypes + self.doc.mime_type = mimetypes.guess_type(self.doc.file_name)[0] or 'application/unknown' diff --git a/cgi-bin/core/doctype/module_def/module_def.py b/cgi-bin/core/doctype/module_def/module_def.py index 9d78fc68a5..b6e6cd5c2e 100644 --- a/cgi-bin/core/doctype/module_def/module_def.py +++ b/cgi-bin/core/doctype/module_def/module_def.py @@ -43,7 +43,7 @@ class DocType: r.save(1) def on_update(self, from_update=0): - import webnotes.defs + from webnotes import defs from webnotes.utils.transfer import in_transfer if (not in_transfer) and getattr(defs,'developer_mode', 0): diff --git a/cgi-bin/core/doctype/profile/profile.txt b/cgi-bin/core/doctype/profile/profile.txt index fd5cd3722f..3f117123e0 100644 --- a/cgi-bin/core/doctype/profile/profile.txt +++ b/cgi-bin/core/doctype/profile/profile.txt @@ -5,25 +5,22 @@ { 'creation': '2009-05-12 11:19:11', 'docstatus': 0, - 'modified': '2010-12-21 11:07:20', - 'modified_by': 'sneha@webnotestech.com', + 'modified': '2011-08-25 14:02:26', + 'modified_by': 'Administrator', 'owner': 'Administrator' }, # These values are common for all DocType { - '_last_update': '1303708853', + '_last_update': '1311340897', 'allow_attach': 1, 'allow_copy': 0, 'allow_email': 0, 'allow_print': 0, - 'client_script': 'cur_frm.cscript[\'Change Password\']= function(doc, cdt, cdn) {\n var error = false;\n if ((!doc.new_password)||(!doc.retype_new_password)){\n alert("Both fields are required!");\n error = true;\n }\n if (doc.new_password.length<4) {\n alert("Password must be atleast 4 characters long");\n error = true;\n }\n if(doc.new_password!=doc.retype_new_password) {\n alert("Passwords must match");\n error = true;\n }\n if(!/[A-Z]/.test(doc.new_password) || !/[0-9]/.test(doc.new_password) || !/[\\W_]/.test(doc.new_password)) {\n msgprint(\'New password must contain atleast 1 capital letter, 1 numeric and 1 special character.\');\n error = true;\n doc.new_password = \'\';\n refresh_field(\'new_password\');\n }\n if(!error) {\n cur_frm.runscript(\'update_password\', \'\', function(r,t) {\n\tdoc.new_password = \'\';\n\tdoc.retype_new_password = \'\';\n refresh_many([\'new_password\',\'retype_new_password\']);\n });\n }\n}\n\ncur_frm.cscript.validate = function(doc, cdt, cdn) {\n doc.new_password = \'\';\n doc.retype_new_password = \'\';\n}', 'colour': 'White:FFF', 'doctype': 'DocType', 'hide_heading': 0, 'hide_toolbar': 0, - 'idx': 0, - 'in_create': 1, 'issingle': 0, 'istable': 0, 'max_attachments': 1, @@ -31,10 +28,9 @@ 'name': '__common__', 'print_outline': 'Yes', 'read_only': 0, - 'section_style': 'Tray', - 'server_code_error': ' ', + 'search_fields': 'first_name, last_name', 'show_in_menu': 0, - 'version': 25 + 'version': 32 }, # These values are common for all DocField @@ -69,7 +65,6 @@ 'create': 1, 'doctype': 'DocPerm', 'execute': 0, - 'idx': 1, 'permlevel': 0, 'role': 'Administrator', 'submit': 0 @@ -79,7 +74,6 @@ { 'create': 1, 'doctype': 'DocPerm', - 'idx': 2, 'permlevel': 0, 'role': 'System Manager' }, @@ -87,7 +81,6 @@ # DocPerm { 'doctype': 'DocPerm', - 'idx': 3, 'permlevel': 1, 'role': 'Administrator' }, @@ -95,67 +88,17 @@ # DocPerm { 'doctype': 'DocPerm', - 'idx': 4, 'match': 'owner', 'permlevel': 0, 'role': 'All' }, - # DocField - { - 'doctype': 'DocField', - 'fieldtype': 'Section Break', - 'hidden': 0, - 'idx': 1, - 'label': 'Details', - 'oldfieldtype': 'Section Break', - 'permlevel': 0, - 'reqd': 0, - 'search_index': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldtype': 'Column Break', - 'hidden': 0, - 'idx': 2, - 'label': 'Picture', - 'oldfieldtype': 'Column Break', - 'permlevel': 0, - 'reqd': 0, - 'search_index': 0, - 'width': '50%' - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldtype': 'Image', - 'idx': 3, - 'label': 'Profile Picture', - 'oldfieldtype': 'Image', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldtype': 'Column Break', - 'idx': 4, - 'label': 'Contact', - 'oldfieldtype': 'Column Break', - 'permlevel': 0, - 'width': '50%' - }, - # DocField { 'default': '1', 'doctype': 'DocField', 'fieldname': 'enabled', 'fieldtype': 'Check', - 'idx': 5, 'label': 'Enabled', 'oldfieldname': 'enabled', 'oldfieldtype': 'Check', @@ -164,24 +107,40 @@ # DocField { - 'default': '1', 'doctype': 'DocField', - 'fieldname': 'send_email_invite', + 'fieldname': 'password', + 'fieldtype': 'Password', + 'label': 'Password', + 'permlevel': 1, + 'hidden': 1 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'registered', 'fieldtype': 'Check', - 'idx': 6, - 'label': 'Send Email Invite', - 'oldfieldname': 'send_email_invite', - 'oldfieldtype': 'Check', - 'permlevel': 1 + 'label': 'Registered', + 'permlevel': 0, + 'hidden': 1 }, + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'unsubscribed', + 'fieldtype': 'Check', + 'label': 'Unsubscribed', + 'permlevel': 0, + 'hidden': 1 + }, + # DocField { 'doctype': 'DocField', 'fieldname': 'recent_documents', 'fieldtype': 'Text', 'hidden': 1, - 'idx': 8, 'label': 'Recent Documents', 'oldfieldname': 'recent_documents', 'oldfieldtype': 'Text', @@ -195,7 +154,6 @@ 'doctype': 'DocField', 'fieldname': 'first_name', 'fieldtype': 'Data', - 'idx': 9, 'label': 'First Name', 'oldfieldname': 'first_name', 'oldfieldtype': 'Data', @@ -208,7 +166,6 @@ 'doctype': 'DocField', 'fieldname': 'middle_name', 'fieldtype': 'Data', - 'idx': 10, 'label': 'Middle Name (Optional)', 'oldfieldname': 'middle_name', 'oldfieldtype': 'Data', @@ -220,46 +177,17 @@ 'doctype': 'DocField', 'fieldname': 'last_name', 'fieldtype': 'Data', - 'idx': 11, 'label': 'Last Name', 'oldfieldname': 'last_name', 'oldfieldtype': 'Data', 'permlevel': 0 }, - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'email', - 'fieldtype': 'Data', - 'hidden': 0, - 'idx': 12, - 'label': 'Email', - 'oldfieldname': 'email', - 'oldfieldtype': 'Data', - 'permlevel': 0, - 'reqd': 1, - 'search_index': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'birth_date', - 'fieldtype': 'Date', - 'idx': 13, - 'label': 'Birth Date', - 'oldfieldname': 'birth_date', - 'oldfieldtype': 'Date', - 'permlevel': 0 - }, - # DocField { 'doctype': 'DocField', 'fieldname': 'gender', 'fieldtype': 'Select', - 'idx': 14, 'label': 'Gender', 'oldfieldname': 'gender', 'oldfieldtype': 'Select', @@ -271,51 +199,74 @@ # DocField { 'doctype': 'DocField', - 'fieldname': 'occupation', - 'fieldtype': 'Data', - 'idx': 15, - 'label': 'Designation', - 'oldfieldname': 'occupation', - 'oldfieldtype': 'Data', - 'permlevel': 0, - 'search_index': 0 + 'fieldtype': 'Column Break', + 'oldfieldtype': 'Column Break', + 'permlevel': 1, + 'width': '50%' }, + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'email', + 'fieldtype': 'Data', + 'hidden': 0, + 'label': 'Email', + 'oldfieldname': 'email', + 'oldfieldtype': 'Data', + 'permlevel': 0, + 'reqd': 1, + 'search_index': 0 + }, + # DocField { 'doctype': 'DocField', 'fieldname': 'bio', 'fieldtype': 'Text', - 'idx': 16, 'label': 'Bio', 'oldfieldname': 'bio', 'oldfieldtype': 'Text', 'permlevel': 0, - 'search_index': 0 + 'search_index': 0, + 'hidden': 1 }, + # DocField { 'doctype': 'DocField', 'fieldname': 'interests', 'fieldtype': 'Text', - 'idx': 17, 'label': 'Interests', 'oldfieldname': 'interests', 'oldfieldtype': 'Text', + 'permlevel': 0, + 'hidden': 1 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'birth_date', + 'fieldtype': 'Date', + 'label': 'Birth Date', + 'oldfieldname': 'birth_date', + 'oldfieldtype': 'Date', 'permlevel': 0 }, + # DocField { 'doctype': 'DocField', 'fieldname': 'activities', 'fieldtype': 'Text', - 'idx': 18, 'label': 'Activities', 'oldfieldname': 'activities', 'oldfieldtype': 'Text', - 'permlevel': 0 + 'permlevel': 0, + 'hidden': 1 }, # DocField @@ -323,7 +274,6 @@ 'doctype': 'DocField', 'fieldname': 'messanger_status', 'fieldtype': 'Data', - 'idx': 19, 'label': 'Messanger Status', 'oldfieldname': 'messanger_status', 'oldfieldtype': 'Data', @@ -331,60 +281,11 @@ 'search_index': 0 }, - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'home_phone', - 'fieldtype': 'Data', - 'idx': 20, - 'label': 'Home Phone', - 'oldfieldname': 'home_phone', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'office_phone', - 'fieldtype': 'Data', - 'idx': 21, - 'label': 'Office Phone', - 'oldfieldname': 'office_phone', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'extension', - 'fieldtype': 'Data', - 'idx': 22, - 'label': 'Extension', - 'oldfieldname': 'extension', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'cell_no', - 'fieldtype': 'Data', - 'idx': 23, - 'label': 'Cell No', - 'oldfieldname': 'cell_no', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - # DocField { 'doctype': 'DocField', 'fieldname': 'user_type', 'fieldtype': 'Select', - 'idx': 24, 'label': 'User Type', 'oldfieldname': 'user_type', 'oldfieldtype': 'Select', @@ -392,150 +293,27 @@ 'permlevel': 0 }, - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'last_login', - 'fieldtype': 'Read Only', - 'hidden': 0, - 'idx': 25, - 'label': 'Last Login', - 'oldfieldname': 'last_login', - 'oldfieldtype': 'Read Only', - 'permlevel': 0, - 'reqd': 0, - 'search_index': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'last_ip', - 'fieldtype': 'Read Only', - 'idx': 26, - 'label': 'Last IP', - 'oldfieldname': 'last_ip', - 'oldfieldtype': 'Read Only', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldtype': 'Section Break', - 'idx': 27, - 'label': 'Address', - 'oldfieldtype': 'Section Break', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'line_1', - 'fieldtype': 'Data', - 'idx': 28, - 'label': 'Line 1', - 'oldfieldname': 'line_1', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'line_2', - 'fieldtype': 'Data', - 'idx': 29, - 'label': 'Line 2', - 'oldfieldname': 'line_2', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'city', - 'fieldtype': 'Data', - 'idx': 30, - 'label': 'City / Town', - 'oldfieldname': 'city', - 'oldfieldtype': 'Data', - 'permlevel': 0, - 'reqd': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'district', - 'fieldtype': 'Data', - 'idx': 31, - 'label': 'District', - 'oldfieldname': 'district', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'state', - 'fieldtype': 'Data', - 'idx': 32, - 'label': 'State', - 'oldfieldname': 'state', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'country', - 'fieldtype': 'Data', - 'idx': 33, - 'label': 'Country', - 'oldfieldname': 'country', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'pin', - 'fieldtype': 'Data', - 'idx': 34, - 'label': 'Pin', - 'oldfieldname': 'pin', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldtype': 'Section Break', - 'idx': 35, - 'label': 'User Role', - 'oldfieldtype': 'Section Break', - 'permlevel': 1 - }, # DocField { 'doctype': 'DocField', 'fieldtype': 'Section Break', 'hidden': 0, - 'idx': 36, - 'label': 'Roles', 'oldfieldtype': 'Section Break', 'permlevel': 1, 'reqd': 0, 'search_index': 0 }, + + # DocField + { + 'doctype': 'DocField', + 'fieldtype': 'Column Break', + 'label': 'Roles', + 'oldfieldtype': 'Column Break', + 'permlevel': 1, + 'width': '50%' + }, # DocField { @@ -545,7 +323,6 @@ 'fieldname': 'userroles', 'fieldtype': 'Table', 'hidden': 0, - 'idx': 37, 'label': 'User Roles', 'oldfieldname': 'userroles', 'oldfieldtype': 'Table', @@ -558,10 +335,9 @@ # DocField { 'doctype': 'DocField', - 'fieldtype': 'Section Break', - 'idx': 38, + 'fieldtype': 'Column Break', 'label': 'System Defaults', - 'oldfieldtype': 'Section Break', + 'oldfieldtype': 'Column Break', 'permlevel': 1, 'width': '50%' }, @@ -574,7 +350,6 @@ 'fieldname': 'defaults', 'fieldtype': 'Table', 'hidden': 0, - 'idx': 39, 'label': 'Defaults', 'oldfieldname': 'defaults', 'oldfieldtype': 'Table', @@ -588,9 +363,35 @@ { 'doctype': 'DocField', 'fieldtype': 'Section Break', - 'idx': 40, - 'label': 'Password', + 'label': 'Login Details', 'oldfieldtype': 'Section Break', + 'permlevel': 0, + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'login_before', + 'fieldtype': 'Int', + 'label': 'Login Before', + 'permlevel': 0 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'login_after', + 'fieldtype': 'Int', + 'label': 'Login After', + 'permlevel': 0 + }, + + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'restrict_ip', + 'fieldtype': 'Data', + 'label': 'Restrict IP', 'permlevel': 0 }, @@ -598,25 +399,20 @@ { 'doctype': 'DocField', 'fieldtype': 'Column Break', - 'idx': 41, - 'label': 'Change Your Password', 'oldfieldtype': 'Column Break', - 'permlevel': 1, 'width': '50%' }, # DocField { - 'colour': 'Pink:FEF2EA', 'doctype': 'DocField', - 'fieldname': 'password', - 'fieldtype': 'Data', - 'hidden': 1, - 'idx': 42, - 'label': 'Current Password', - 'oldfieldname': 'password', - 'oldfieldtype': 'Data', - 'permlevel': 1, + 'fieldname': 'last_login', + 'fieldtype': 'Read Only', + 'hidden': 0, + 'label': 'Last Login', + 'oldfieldname': 'last_login', + 'oldfieldtype': 'Read Only', + 'permlevel': 0, 'reqd': 0, 'search_index': 0 }, @@ -624,139 +420,23 @@ # DocField { 'doctype': 'DocField', - 'fieldname': 'new_password', - 'fieldtype': 'Password', - 'idx': 43, - 'label': 'New Password', - 'oldfieldname': 'new_password', - 'oldfieldtype': 'Password', - 'permlevel': 1 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'retype_new_password', - 'fieldtype': 'Password', - 'idx': 44, - 'label': 'Retype New Password', - 'oldfieldname': 'retype_new_password', - 'oldfieldtype': 'Password', - 'permlevel': 1 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'password_last_updated', - 'fieldtype': 'Date', - 'hidden': 1, - 'idx': 45, - 'label': 'Password Last Updated', - 'oldfieldname': 'password_last_updated', - 'oldfieldtype': 'Date', - 'permlevel': 1, - 'print_hide': 1 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldtype': 'Button', - 'idx': 46, - 'label': 'Change Password', - 'oldfieldtype': 'Button', - 'permlevel': 1, - 'trigger': 'Client', - 'width': '120px' - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldtype': 'Section Break', - 'idx': 47, - 'label': 'Attachment', - 'oldfieldtype': 'Section Break', - 'permlevel': 1 - }, - - # DocField - { - 'default': '0', - 'doctype': 'DocField', - 'fieldname': 'social_points', - 'fieldtype': 'Int', - 'idx': 48, - 'label': 'Social Points', - 'oldfieldname': 'social_points', - 'oldfieldtype': 'Int', + 'fieldname': 'last_ip', + 'fieldtype': 'Read Only', + 'label': 'Last IP', + 'oldfieldname': 'last_ip', + 'oldfieldtype': 'Read Only', 'permlevel': 0 }, - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'social_badge', - 'fieldtype': 'Data', - 'idx': 49, - 'label': 'Social Badge', - 'oldfieldname': 'social_badge', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'avatar', - 'fieldtype': 'Data', - 'idx': 50, - 'label': 'Avatar', - 'oldfieldname': 'avatar', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldtype': 'HTML', - 'idx': 51, - 'label': 'Attachment HTML', - 'oldfieldtype': 'HTML', - 'options': 'First attachment must be the picture', - 'permlevel': 1 - }, - # DocField { 'doctype': 'DocField', 'fieldname': 'file_list', 'fieldtype': 'Text', 'hidden': 1, - 'idx': 52, 'label': 'File List', 'oldfieldname': 'file_list', 'oldfieldtype': 'Text', 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'fiscal_year', - 'fieldtype': 'Select', - 'hidden': 1, - 'idx': 53, - 'in_filter': 1, - 'label': 'Fiscal Year', - 'no_copy': 1, - 'oldfieldname': 'fiscal_year', - 'oldfieldtype': 'Select', - 'options': 'link:Fiscal Year', - 'permlevel': 0, - 'print_hide': 1, - 'report_hide': 1 - } + } ] \ No newline at end of file diff --git a/cgi-bin/core/doctype/search_criteria/search_criteria.txt b/cgi-bin/core/doctype/search_criteria/search_criteria.txt index 0a4f09e55d..a3ca3ae369 100644 --- a/cgi-bin/core/doctype/search_criteria/search_criteria.txt +++ b/cgi-bin/core/doctype/search_criteria/search_criteria.txt @@ -87,10 +87,13 @@ # DocField { 'doctype': 'DocField', - 'fieldtype': 'Section Break', - 'idx': 1, - 'label': 'Details', - 'oldfieldtype': 'Section Break', + 'fieldname': 'criteria_name', + 'fieldtype': 'Data', + 'hidden': 0, + 'idx': 5, + 'label': 'Criteria Name', + 'oldfieldname': 'criteria_name', + 'oldfieldtype': 'Data', 'permlevel': 0 }, @@ -135,41 +138,6 @@ 'search_index': 1 }, - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'criteria_name', - 'fieldtype': 'Data', - 'hidden': 0, - 'idx': 5, - 'label': 'Criteria Name', - 'oldfieldname': 'criteria_name', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'description', - 'fieldtype': 'Text', - 'idx': 6, - 'label': 'Description', - 'oldfieldname': 'description', - 'oldfieldtype': 'Text', - 'permlevel': 0, - 'width': '300px' - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldtype': 'Section Break', - 'idx': 7, - 'label': 'Query Details', - 'oldfieldtype': 'Section Break', - 'permlevel': 0 - }, # DocField { @@ -189,7 +157,7 @@ 'doctype': 'DocField', 'fieldname': 'filters', 'fieldtype': 'Text', - 'hidden': 0, + 'hidden': 1, 'idx': 9, 'label': 'Filters', 'oldfieldname': 'filters', @@ -288,32 +256,6 @@ 'permlevel': 0 }, - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'graph_series', - 'fieldtype': 'Data', - 'hidden': 0, - 'idx': 17, - 'label': 'Graph Series', - 'oldfieldname': 'graph_series', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'graph_values', - 'fieldtype': 'Data', - 'hidden': 0, - 'idx': 18, - 'label': 'Graph Values', - 'oldfieldname': 'graph_values', - 'oldfieldtype': 'Data', - 'permlevel': 0 - }, - # DocField { 'doctype': 'DocField', diff --git a/cgi-bin/webnotes/auth.py b/cgi-bin/webnotes/auth.py index d2b35075f9..6858151560 100644 --- a/cgi-bin/webnotes/auth.py +++ b/cgi-bin/webnotes/auth.py @@ -104,7 +104,7 @@ class HTTPRequest: else: db_name = getattr(webnotes.defs,'default_db_name','') - webnotes.conn = webnotes.db.Database(user = db_name,password = getattr(webnotes.defs,'db_password', None)) + webnotes.conn = webnotes.db.Database(user = db_name,password = getattr(webnotes.defs,'db_password', '')) webnotes.ac_name = ac_name # ================================================================================= @@ -127,8 +127,9 @@ class LoginManager: # --------------------------- def post_login(self): - self.validate_ip_address() self.run_trigger() + self.validate_ip_address() + self.validate_hour() # check password # -------------- @@ -186,16 +187,38 @@ class LoginManager: # ------------- def validate_ip_address(self): - try: - ip = webnotes.conn.sql("select ip_address from tabProfile where name = '%s'" % self.user)[0][0] or '' - except: return + ip_list = webnotes.conn.get_value('Profile', self.user, 'restrict_ip', ignore=True) + + if not ip_list: + return + + ip_list = ip_list.replace(",", "\n").split('\n') + ip_list = [i.strip() for i in ip_list] + + for ip in ip_list: + if webnotes.remote_ip.startswith(ip): + return - ip = ip.replace(",", "\n").split('\n') - ip = [i.strip() for i in ip] + webnotes.msgprint('Not allowed from this IP Address', raise_exception=1) + + def validate_hour(self): + """ + check if user is logging in during restricted hours + """ + login_before = int(webnotes.conn.get_value('Profile', self.user, 'login_before', ignore=True) or 0) + login_after = int(webnotes.conn.get_value('Profile', self.user, 'login_after', ignore=True) or 0) + + if not (login_before or login_after): + return - if ret and ip: - if not (webnotes.remote_ip.startswith(ip[0]) or (webnotes.remote_ip in ip)): - raise Exception, 'Not allowed from this IP Address' + from webnotes.utils import now_datetime + current_hour = int(now_datetime().strftime('%H')) + + if login_before and current_hour > login_before: + webnotes.msgprint('Not allowed to login after restricted hour', raise_exception=1) + + if login_after and current_hour < login_after: + webnotes.msgprint('Not allowed to login before restricted hour', raise_exception=1) # login as guest # -------------- diff --git a/cgi-bin/webnotes/db.py b/cgi-bin/webnotes/db.py index 45837a2051..f288714d47 100644 --- a/cgi-bin/webnotes/db.py +++ b/cgi-bin/webnotes/db.py @@ -255,7 +255,7 @@ class Database: # ====================================================================================== # get a single value from a record - def get_value(self, doctype, docname, fieldname): + def get_value(self, doctype, docname, fieldname, ignore=None): """ Get a single / multiple value from a record. @@ -266,8 +266,13 @@ class Database: if docname and (docname!=doctype or docname=='DocType'): if type(fieldname) in (list, tuple): fl = '`, `'.join(fieldname) - - r = self.sql("select `%s` from `tab%s` where name='%s'" % (fl, doctype, docname)) + try: + r = self.sql("select `%s` from `tab%s` where name='%s'" % (fl, doctype, docname)) + except Exception, e: + if e.args[0]==1054 and ignore: + return None + else: + raise e return r and (len(r[0]) > 1 and r[0] or r[0][0]) or None else: if type(fieldname) in (list, tuple): diff --git a/cgi-bin/webnotes/model/doclist.py b/cgi-bin/webnotes/model/doclist.py index c8df02660a..5e270a69cf 100644 --- a/cgi-bin/webnotes/model/doclist.py +++ b/cgi-bin/webnotes/model/doclist.py @@ -18,7 +18,7 @@ class DocList: self.to_docstatus = 0 if dt and dn: self.load_from_db(dt, dn) - + def load_from_db(self, dt, dn): """ Load doclist from dt @@ -34,15 +34,15 @@ class DocList: doclist = [doc,] for t in tablefields: doclist += getchildren(doc.name, t[0], t[1], dt, prefix=prefix) - + self.docs = docs - + def __iter__(self): """ Make this iterable """ return self.docs.__iter__() - + def from_compressed(self, data, docname): """ Expand called from client @@ -50,13 +50,13 @@ class DocList: from webnotes.model.utils import expand self.docs = expand(data) self.objectify(docname) - + def objectify(self, docname=None): """ Converts self.docs from a list of dicts to list of Documents """ from webnotes.model.doc import Document - + self.docs = [Document(fielddata=d) for d in self.docs] if not docname: self.doc, self.children = self.docs[0], self.docs[1:] @@ -69,21 +69,20 @@ class DocList: self.doc = d else: self.children.append(d) - # catch all if no self.doc if not self.doc: self.doc, self.children = self.docs[0], self.docs[1:] - + def make_obj(self): """ Create a DocType object """ if self.obj: return self.obj - + from webnotes.model.code import get_obj self.obj = get_obj(doc=self.doc, doclist=self.children) return self.obj - + def next(self): """ Next doc @@ -104,13 +103,13 @@ class DocList: if (not is_single(self.doc.doctype)) and (not cint(self.doc.fields.get('__islocal'))): tmp = webnotes.conn.sql(""" - SELECT modified FROM `tab%s` WHERE name="%s" for update""" + SELECT modified FROM `tab%s` WHERE name="%s" for update""" % (self.doc.doctype, self.doc.name)) if tmp and str(tmp[0][0]) != str(self.doc.modified): webnotes.msgprint(""" - Document has been modified after you have opened it. - To maintain the integrity of the data, you will not be able to save your changes. + Document has been modified after you have opened it. + To maintain the integrity of the data, you will not be able to save your changes. Please refresh this document. [%s/%s]""" % (tmp[0][0], self.doc.modified), raise_exception=1) def check_permission(self): @@ -119,7 +118,7 @@ class DocList: """ if not self.doc.check_perm(verbose=1): webnotes.msgprint("Not enough permission to save %s" % self.doc.doctype, raise_exception=1) - + def check_links(self): """ Checks integrity of links (throws exception if links are invalid) @@ -130,11 +129,11 @@ class DocList: ref[d.doctype] = d.make_link_list() err_list += d.validate_links(ref[d.doctype]) - + if err_list: - webnotes.msgprint("""[Link Validation] Could not find the following values: %s. + webnotes.msgprint("""[Link Validation] Could not find the following values: %s. Please correct and resave. Document Not Saved.""" % ', '.join(err_list), raise_exception=1) - + def update_timestamps_and_docstatus(self): """ Update owner, creation, modified_by, modified, docstatus @@ -142,17 +141,17 @@ class DocList: from webnotes.utils import now ts = now() user = webnotes.__dict__.get('session', {}).get('user') or 'Administrator' - + for d in self.docs: if self.doc.__islocal: d.owner = user d.creation = ts - + d.modified_by = user d.modified = ts if d.docstatus != 2: # don't update deleted d.docstatus = self.to_docstatus - + def prepare_for_save(self, check_links): """ Set owner, modified etc before saving @@ -175,7 +174,7 @@ class DocList: from webnotes.model.triggers import fire_event fire_event(self.doc, method) - + def save_main(self): """ Save the main doc @@ -184,7 +183,7 @@ class DocList: self.doc.save(cint(self.doc.__islocal)) except NameError, e: webnotes.msgprint('%s "%s" already exists' % (self.doc.doctype, self.doc.name)) - + # prompt if cancelled if webnotes.conn.get_value(self.doc.doctype, self.doc.name, 'docstatus')==2: webnotes.msgprint('[%s "%s" has been cancelled]' % (self.doc.doctype, self.doc.name)) @@ -197,7 +196,7 @@ class DocList: """ for d in self.children: deleted, local = d.fields.get('__deleted',0), d.fields.get('__islocal',0) - + if cint(local) and cint(deleted): pass @@ -206,7 +205,7 @@ class DocList: d.parent = self.doc.name # rename if reqd d.parenttype = self.doc.doctype - d.save(new = cint(local)) + d.save(new = cint(local)) def save(self, check_links=1): """ @@ -217,7 +216,7 @@ class DocList: self.save_main() self.save_children() self.run_method('on_update') - + def submit(self): """ Save & Submit - set docstatus = 1, run "on_submit" @@ -227,7 +226,7 @@ class DocList: self.to_docstatus = 1 self.save() self.run_method('on_submit') - + def cancel(self): """ Cancel - set docstatus 2, run "on_cancel" @@ -239,7 +238,7 @@ class DocList: self.save_main() self.save_children() self.run_method('on_cancel') - + def update_after_submit(self): """ Update after submit - some values changed after submit @@ -260,11 +259,11 @@ def getlist(doclist, parentfield): """ import webnotes.model.utils return webnotes.model.utils.getlist(doclist, parentfield) - + def copy_doclist(doclist, no_copy = []): """ Make a copy of the doclist """ import webnotes.model.utils return webnotes.model.utils.copy_doclist(doclist, no_copy) - + diff --git a/cgi-bin/webnotes/model/utils.py b/cgi-bin/webnotes/model/utils.py index 2d019273f1..7e0fe29130 100644 --- a/cgi-bin/webnotes/model/utils.py +++ b/cgi-bin/webnotes/model/utils.py @@ -1,7 +1,7 @@ """ Model utilities, unclassified functions """ - + def expand(docs): """ Expand a doclist sent from the client side. (Internally used by the request handler) @@ -25,12 +25,12 @@ def compress(doclist): """ Compress a doclist before sending it to the client side. (Internally used by the request handler) - """ + """ if doclist and hasattr(doclist[0],'fields'): docs = [d.fields for d in doclist] else: docs = doclist - + kl, vl = {}, [] for d in docs: dt = d['doctype'] @@ -38,10 +38,10 @@ def compress(doclist): fl = d.keys() forbidden = ['server_code_compiled'] nl = ['doctype','localname','__oldparent','__unsaved'] - + # add client script for doctype, doctype due to ambiguity if dt=='DocType': nl.append('__client_script') - + for f in fl: if not (f in nl) and not (f in forbidden): nl.append(f) @@ -64,21 +64,24 @@ def compress(doclist): def getlist(doclist, field): """ Filter a list of records for a specific field from the full doclist - + Example:: - - # find all phone call details + + # find all phone call details dl = getlist(self.doclist, 'contact_updates') pl = [] for d in dl: if d.type=='Phone': pl.append(d) """ - + from webnotes.utils import cint l = [] for d in doclist: if d.parent and (not d.parent.lower().startswith('old_parent:')) and d.parentfield == field: l.append(d) + + l.sort(lambda a, b: cint(a.idx) - cint(b.idx)) + return l # Copy doclist @@ -90,31 +93,31 @@ def copy_doclist(doclist, no_copy = []): Pass fields that are not to be copied in `no_copy` """ from webnotes.model.doc import Document - + cl = [] - + # main doc c = Document(fielddata = doclist[0].fields.copy()) - + # clear no_copy fields - for f in no_copy: + for f in no_copy: if c.fields.has_key(f): c.fields[f] = None - + c.name = None c.save(1) cl.append(c) - + # new parent name parent = c.name - + # children for d in doclist[1:]: c = Document(fielddata = d.fields.copy()) c.name = None - + # clear no_copy fields - for f in no_copy: + for f in no_copy: if c.fields.has_key(f): c.fields[f] = None @@ -138,18 +141,18 @@ def _make_html(doc, link_list): from webnotes.utils import cstr out = '' for k in doc.fields.keys(): - if k!='server_code_compiled': + if k!='server_code_compiled': v = cstr(doc.fields[k]) - + # link field if v and (k in link_list.keys()): dt = link_list[k] if type(dt)==str and dt.startswith('link:'): dt = dt[5:] - v = '%s' % (dt, v, v) - + v = '%s' % (dt, v, v) + out += '\t\n' % (cstr(k), v) - + out += '
%s%s
' return out @@ -159,13 +162,13 @@ def to_html(doclist): """ out = '' link_lists = {} - + for d in doclist: if not link_lists.get(d.doctype): link_lists[d.doctype] = d.make_link_list() out += _make_html(d, link_lists[d.doctype]) - + return out def commonify_doclist(doclist, with_comments=1): @@ -183,15 +186,17 @@ def commonify_doclist(doclist, with_comments=1): c[k] = doclist[0][k] return c - def strip_common(d): - for k in common_keys: + def strip_common_and_idx(d): + for k in common_keys: if k in d: del d[k] + + if 'idx' in d: del d['idx'] return d def make_common_dicts(doclist): - + common_dict = {} # one per doctype - + # make common dicts for all records for d in doclist: if not d['doctype'] in common_dict: @@ -206,15 +211,15 @@ def commonify_doclist(doclist, with_comments=1): common_dict = make_common_dicts(doclist) # make docs - final = [] + final = [] for d in doclist: - f = strip_common(get_diff_dict(common_dict[d['doctype']], d)) + f = strip_common_and_idx(get_diff_dict(common_dict[d['doctype']], d)) f['doctype'] = d['doctype'] # keep doctype! - + # strip name for child records (only an auto generated number!) if f['doctype'] != doclist[0]['doctype']: del f['name'] - + if with_comments: f['##comment'] = d['doctype'] + ('name' in f and (', ' + f['name']) or '') final.append(f) @@ -225,37 +230,51 @@ def commonify_doclist(doclist, with_comments=1): d['name']='__common__' if with_comments: d['##comment'] = 'These values are common for all ' + d['doctype'] - commons.append(strip_common(d)) - + commons.append(strip_common_and_idx(d)) + common_values = make_common(doclist) return [common_values]+commons+final - + def uncommonify_doclist(dl): """ Expands an commonified doclist """ + # first one has common values common_values = dl[0] common_dict = {} final = [] + idx_dict = {} for d in dl[1:]: if 'name' in d and d['name']=='__common__': + # common for a doctype - del d['name'] common_dict[d['doctype']] = d else: + dt = d['doctype'] + if not dt in idx_dict: idx_dict[dt] = 0; d1 = common_values.copy() - d1.update(common_dict[d['doctype']]) + + # update from common and global + d1.update(common_dict[dt]) d1.update(d) + + # idx by sequence + d1['idx'] = idx_dict[dt] + + # increment idx + idx_dict[dt] += 1 + final.append(d1) return final - + def pprint_doclist(doclist, with_comments = 1): """ Pretty Prints a doclist with common keys separated and comments """ from webnotes.utils import pprint_dict - + dictlist =[pprint_dict(d) for d in commonify_doclist(doclist, with_comments)] title = '# '+doclist[0]['doctype']+', '+doclist[0]['name'] return title + '\n[\n' + ',\n'.join(dictlist) + '\n]' @@ -268,5 +287,5 @@ def peval_doclist(txt): return uncommonify_doclist(eval(txt)) else: return eval(txt) - + return uncommonify_doclist(eval(txt)) diff --git a/cgi-bin/webnotes/utils/__init__.py b/cgi-bin/webnotes/utils/__init__.py index 1376ed7d41..6e342f1802 100644 --- a/cgi-bin/webnotes/utils/__init__.py +++ b/cgi-bin/webnotes/utils/__init__.py @@ -119,7 +119,7 @@ def getdate(string_date): else: return '' -def add_days(date, days): +def add_days(date, days, format='string'): """ Adds `days` to the given `string_date` """ @@ -130,7 +130,11 @@ def add_days(date, days): if type(date) not in (datetime.datetime, datetime.date): date = getdate(date) - return (date + datetime.timedelta(days)).strftime('%Y-%m-%d') + dt = date + datetime.timedelta(days) + if format=='string': + return dt.strftime('%Y-%m-%d') + else: + return dt def add_months(string_date, months): import datetime diff --git a/cgi-bin/webnotes/utils/email_lib/receive.py b/cgi-bin/webnotes/utils/email_lib/receive.py index 7207d836f0..d6cc347998 100644 --- a/cgi-bin/webnotes/utils/email_lib/receive.py +++ b/cgi-bin/webnotes/utils/email_lib/receive.py @@ -39,8 +39,11 @@ class IncomingMail: """ get utf-8 encoded part content """ - return unicode(part.get_payload(decode=True),str(charset),"ignore").encode('utf8','replace') - + try: + return unicode(part.get_payload(decode=True),str(charset),"ignore").encode('utf8','replace') + except LookupError, e: + return part.get_payload() + def get_attachment(self, part, charset): """ Extracts an attachment @@ -128,7 +131,10 @@ class POP3Mailbox: num = len(self.pop.list()[1]) for m in range(num): msg = self.pop.retr(m+1) - self.process_message(IncomingMail('\n'.join(msg[1]))) + try: + self.process_message(IncomingMail('\n'.join(msg[1]))) + except: + pass self.pop.dele(m+1) self.pop.quit() diff --git a/cgi-bin/webnotes/widgets/page.py b/cgi-bin/webnotes/widgets/page.py index 0cad0b72d6..de31fb9fd7 100644 --- a/cgi-bin/webnotes/widgets/page.py +++ b/cgi-bin/webnotes/widgets/page.py @@ -19,7 +19,7 @@ class Page: Loads page info from files in module """ # load js - doc.fields['__script'] = module.get_doc_file('page',doc.name,'.js').read() + doc.fields['__script'] = module.get_doc_file('page',doc.name,'.js').read() or doc.script doc.script = None # load css diff --git a/cgi-bin/webnotes/widgets/query_builder.py b/cgi-bin/webnotes/widgets/query_builder.py index 72e320cd70..6b4196b6d7 100644 --- a/cgi-bin/webnotes/widgets/query_builder.py +++ b/cgi-bin/webnotes/widgets/query_builder.py @@ -14,7 +14,7 @@ def get_search_criteria_list(dt): def load_report_list(): webnotes.response['rep_list'] = get_search_criteria_list(form.getvalue('dt')) - + # Get, scrub metadata # ==================================================================== @@ -37,20 +37,20 @@ def get_parent_dt(dt): def get_sql_meta(tl): std_columns = { - 'owner':('Owner', '', '', '100'), - 'creation':('Created on', 'Date', '', '100'), - 'modified':('Last modified on', 'Date', '', '100'), + 'owner':('Owner', '', '', '100'), + 'creation':('Created on', 'Date', '', '100'), + 'modified':('Last modified on', 'Date', '', '100'), 'modified_by':('Modified By', '', '', '100') } - + meta = {} - + for dt in tl: meta[dt] = std_columns.copy() # for table doctype, the ID is the parent id pdt = get_parent_dt(dt) - if pdt: + if pdt: meta[dt]['parent'] = ('ID', 'Link', pdt, '200') # get the field properties from DocField @@ -58,10 +58,10 @@ def get_sql_meta(tl): for r in res: if r[0]: meta[dt][r[0]] = (r[1], r[2], r[3], r[4]); - + # name meta[dt]['name'] = ('ID', 'Link', dt, '200') - + return meta # Additional conditions to fulfill match permission rules @@ -80,12 +80,12 @@ def getmatchcondition(dt, ud, ur): return '' return ' OR '.join(cond) - + def add_match_conditions(q, tl, ur, ud): sl = [] for dt in tl: s = getmatchcondition(dt, ud, ur) - if s: + if s: sl.append(s) # insert the conditions @@ -94,13 +94,13 @@ def add_match_conditions(q, tl, ur, ud): condition_end = q.find('ORDER BY')!=-1 and 'ORDER BY' or 'LIMIT' condition_end = q.find('GROUP BY')!=-1 and 'GROUP BY' or condition_end - + if q.find('ORDER BY')!=-1 or q.find('LIMIT')!=-1 or q.find('GROUP BY')!=-1: # if query continues beyond conditions q = q.split(condition_end) q = q[0] + condition_st + '(' + ' OR '.join(sl) + ') ' + condition_end + q[1] else: q = q + condition_st + '(' + ' OR '.join(sl) + ')' - + return q # execute server-side script from Search Criteria @@ -111,7 +111,7 @@ def exec_report(code, res, colnames=[], colwidths=[], coltypes=[], coloptions=[] for c in colnames: col_idx[c] = i i+=1 - + # load globals (api) from webnotes import * from webnotes.utils import * @@ -127,12 +127,12 @@ def exec_report(code, res, colnames=[], colwidths=[], coltypes=[], coloptions=[] NEWLINE = '\n' exec str(code) - + if out!=None: res = out return res, style, header_html, footer_html, page_template - + # ==================================================================== def guess_type(m): @@ -146,7 +146,7 @@ def guess_type(m): return 'Date' else: return 'Data' - + def build_description_simple(): colnames, coltypes, coloptions, colwidths = [], [], [], [] @@ -155,7 +155,7 @@ def build_description_simple(): coltypes.append(guess_type[m[0]]) coloptions.append('') colwidths.append('100') - + return colnames, coltypes, coloptions, colwidths # ==================================================================== @@ -180,27 +180,27 @@ def build_description_standard(meta, tl): if (not dt) and merged_meta.get(fn): # no "AS" given, find type from merged description - + desc = merged_meta[fn] colnames.append(desc[0] or fn) coltypes.append(desc[1] or '') coloptions.append(desc[2] or '') colwidths.append(desc[3] or '100') - + elif meta.get(dt,{}).has_key(fn): # type specified for a multi-table join # usually from Report Builder - + desc = meta[dt][fn] colnames.append(desc[0] or fn) coltypes.append(desc[1] or '') coloptions.append(desc[2] or '') colwidths.append(desc[3] or '100') - + else: # nothing found # guess - + colnames.append(fn) coltypes.append(guess_type(f[1])) coloptions.append('') @@ -214,21 +214,21 @@ def build_description_standard(meta, tl): def runquery(q='', ret=0, from_export=0): import webnotes.utils - formatted = cint(form.getvalue('formatted')) - + formatted = cint(form.getvalue('formatted')) + # CASE A: Simple Query # -------------------- if form.getvalue('simple_query') or form.getvalue('is_simple'): - q = form.getvalue('simple_query') or form.getvalue('query') + if not q: q = form.getvalue('simple_query') or form.getvalue('query') if q.split()[0].lower() != 'select': raise Exception, 'Query must be a SELECT' - + as_dict = cint(form.getvalue('as_dict')) res = sql(q, as_dict = as_dict, as_list = not as_dict, formatted=formatted) - + # build colnames etc from metadata colnames, coltypes, coloptions, colwidths = [], [], [], [] - + # CASE B: Standard Query # ----------------------- else: @@ -236,17 +236,17 @@ def runquery(q='', ret=0, from_export=0): tl = get_sql_tables(q) meta = get_sql_meta(tl) - + q = add_match_conditions(q, tl, webnotes.user.roles, webnotes.user.get_defaults()) - + # replace special variables q = q.replace('__user', session['user']) q = q.replace('__today', webnotes.utils.nowdate()) - + res = sql(q, as_list=1, formatted=formatted) colnames, coltypes, coloptions, colwidths = build_description_standard(meta, tl) - + # run server script # ----------------- style, header_html, footer_html, page_template = '', '', '', '' @@ -254,15 +254,15 @@ def runquery(q='', ret=0, from_export=0): sc_id = form.getvalue('sc_id') from webnotes.model.code import get_code sc_details = webnotes.conn.sql("select module, standard, server_script from `tabSearch Criteria` where name=%s", sc_id)[0] - if sc_details[1]!='No': + if sc_details[1]!='No': code = get_code(sc_details[0], 'Search Criteria', sc_id, 'py') else: code = sc_details[2] - + if code: filter_values = form.has_key('filter_values') and eval(form.getvalue('filter_values','')) or {} res, style, header_html, footer_html, page_template = exec_report(code, res, colnames, colwidths, coltypes, coloptions, filter_values, q, from_export) - + out['colnames'] = colnames out['coltypes'] = coltypes out['coloptions'] = coloptions @@ -270,17 +270,17 @@ def runquery(q='', ret=0, from_export=0): out['header_html'] = header_html out['footer_html'] = footer_html out['page_template'] = page_template - + if style: out['style'] = style - + # just the data - return if ret==1: - return res + return res out['values'] = res - # return num of entries + # return num of entries qm = form.has_key('query_max') and form.getvalue('query_max') or '' if qm and qm.strip(): if qm.split()[0].lower() != 'select': @@ -298,31 +298,31 @@ def runquery_csv(): # run query res = runquery(from_export = 1) - + q = form.getvalue('query') - + rep_name = form.getvalue('report_name') if not form.has_key('simple_query'): # Report Name if not rep_name: rep_name = get_sql_tables(q)[0] - + if not rep_name: rep_name = 'DataExport' - + # Headings heads = [] - + rows = [[rep_name], out['colnames']] + out['values'] - + from cStringIO import StringIO import csv - + f = StringIO() writer = csv.writer(f) for r in rows: writer.writerow(r) - + f.seek(0) out['result'] = f.read() out['type'] = 'csv' diff --git a/cgi-bin/webnotes/widgets/search.py b/cgi-bin/webnotes/widgets/search.py index 9d8da2b790..bfd1c08486 100644 --- a/cgi-bin/webnotes/widgets/search.py +++ b/cgi-bin/webnotes/widgets/search.py @@ -22,16 +22,16 @@ def getsearchfields(): webnotes.response['searchfields'] = [['name', 'ID', 'Data', '']] + res def make_query(fields, dt, key, txt, start, length): - return """SELECT %(fields)s - FROM `tab%(dt)s` + return """SELECT %(fields)s + FROM `tab%(dt)s` WHERE `tab%(dt)s`.`%(key)s` LIKE '%(txt)s' AND `tab%(dt)s`.docstatus != 2 - ORDER BY `tab%(dt)s`.`%(key)s` + ORDER BY `tab%(dt)s`.`%(key)s` DESC LIMIT %(start)s, %(len)s """ % { 'fields': fields, 'dt': dt, 'key': key, 'txt': txt + '%', - 'start': start, + 'start': start, 'len': length } @@ -48,7 +48,7 @@ def get_std_fields_list(dt, key): def build_for_autosuggest(res): from webnotes.utils import cstr - + results = [] for r in res: info = '' @@ -56,10 +56,10 @@ def build_for_autosuggest(res): info = ','.join([cstr(t) for t in r[1:]]) if len(info) > 30: info = info[:30] + '...' - + results.append({'id':r[0], 'value':r[0], 'info':info}) return results - + def scrub_custom_query(query, key, txt): if '%(key)s' in query: query = query.replace('%(key)s', key) @@ -74,7 +74,7 @@ def search_link(): txt = webnotes.form.getvalue('txt') dt = webnotes.form.getvalue('dt') query = webnotes.form.getvalue('query') - + if query: res = webnotes.conn.sql(scrub_custom_query(query, 'name', txt)) else: @@ -97,5 +97,5 @@ def search_widget(): query = scrub_custom_query(user_query, key, txt) else: query = make_query(', '.join(get_std_fields_list(dt, key)), dt, key, txt, webnotes.form.getvalue('start') or 0, webnotes.form.getvalue('page_len') or 50) - + webnotes.widgets.query_builder.runquery(query) diff --git a/css/default.css b/css/default.css index 1206c8fe72..13229518d5 100644 --- a/css/default.css +++ b/css/default.css @@ -1244,13 +1244,11 @@ ul.box_tabs li.box_tab_selected a { /* background:url("../images/ui/rc/tab-right-CCC.gif") no-repeat right top; */ } -/******* jqPlot ***********/ - /*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/ .jqplot-target { position: relative; color: #666666; - font-family: Arial, Helvetica, sans-serif; + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; font-size: 1em; /* height: 300px; width: 400px;*/ @@ -1308,6 +1306,15 @@ ul.box_tabs li.box_tab_selected a { text-align: right; } +.jqplot-yaxis-tick.jqplot-breakTick { + right: -20px; + margin-right: 0px; + padding:1px 5px 1px 5px; +/* background-color: white;*/ + z-index: 2; + font-size: 1.5em; +} + .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { left: 0px; /* initial position untill tick is drawn in proper place */ @@ -1317,6 +1324,15 @@ ul.box_tabs li.box_tab_selected a { text-align: left; } +.jqplot-meterGauge-tick { + font-size: 0.75em; + color: #999999; +} + +.jqplot-meterGauge-label { + font-size: 1em; + color: #999999; +} .jqplot-xaxis-label { margin-top: 10px; font-size: 11pt; @@ -1342,6 +1358,13 @@ ul.box_tabs li.box_tab_selected a { position: absolute; } +table.jqplot-table-legend { + margin-top: 12px; + margin-bottom: 12px; + margin-left: 12px; + margin-right: 12px; +} + table.jqplot-table-legend, table.jqplot-cursor-legend { background-color: rgba(255,255,255,0.6); border: 1px solid #cccccc; @@ -1353,18 +1376,22 @@ td.jqplot-table-legend { vertical-align:middle; } +td.jqplot-seriesToggle:hover, td.jqplot-seriesToggle:active { + cursor: pointer; +} + td.jqplot-table-legend > div { - border:1px solid #cccccc; - padding:0.2em; + border: 1px solid #cccccc; + padding:1px; } div.jqplot-table-legend-swatch { width:0px; height:0px; - border-top-width: 0.35em; - border-bottom-width: 0.35em; - border-left-width: 0.6em; - border-right-width: 0.6em; + border-top-width: 5px; + border-bottom-width: 5px; + border-left-width: 6px; + border-right-width: 6px; border-top-style: solid; border-bottom-style: solid; border-left-style: solid; @@ -1402,6 +1429,7 @@ table.jqplot-cursor-tooltip { .jqplot-point-label { font-size: 0.75em; + z-index: 2; } td.jqplot-cursor-legend-swatch { @@ -1414,6 +1442,35 @@ width:1.2em; height:0.7em; } +.jqplot-error { +/* Styles added to the plot target container when there is an error go here.*/ + text-align: center; +} + +.jqplot-error-message { +/* Styling of the custom error message div goes here.*/ + position: relative; + top: 46%; + display: inline-block; +} + +div.jqplot-bubble-label { + font-size: 0.8em; +/* background: rgba(90%, 90%, 90%, 0.15);*/ + padding-left: 2px; + padding-right: 2px; + color: rgb(20%, 20%, 20%); +} + +div.jqplot-bubble-label.jqplot-bubble-label-highlight { + background: rgba(90%, 90%, 90%, 0.7); +} + +div.jqplot-noData-container { + text-align: center; + background-color: rgba(96%, 96%, 96%, 0.3); +} + /** general icons **/ diff --git a/css/jqplot.css b/css/jqplot.css index c1882b5286..c9bf6f923b 100644 --- a/css/jqplot.css +++ b/css/jqplot.css @@ -1,10 +1,8 @@ -/******* jqPlot ***********/ - /*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/ .jqplot-target { position: relative; color: #666666; - font-family: Arial, Helvetica, sans-serif; + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; font-size: 1em; /* height: 300px; width: 400px;*/ @@ -62,6 +60,15 @@ text-align: right; } +.jqplot-yaxis-tick.jqplot-breakTick { + right: -20px; + margin-right: 0px; + padding:1px 5px 1px 5px; +/* background-color: white;*/ + z-index: 2; + font-size: 1.5em; +} + .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { left: 0px; /* initial position untill tick is drawn in proper place */ @@ -71,6 +78,15 @@ text-align: left; } +.jqplot-meterGauge-tick { + font-size: 0.75em; + color: #999999; +} + +.jqplot-meterGauge-label { + font-size: 1em; + color: #999999; +} .jqplot-xaxis-label { margin-top: 10px; font-size: 11pt; @@ -96,6 +112,13 @@ position: absolute; } +table.jqplot-table-legend { + margin-top: 12px; + margin-bottom: 12px; + margin-left: 12px; + margin-right: 12px; +} + table.jqplot-table-legend, table.jqplot-cursor-legend { background-color: rgba(255,255,255,0.6); border: 1px solid #cccccc; @@ -107,18 +130,22 @@ td.jqplot-table-legend { vertical-align:middle; } +td.jqplot-seriesToggle:hover, td.jqplot-seriesToggle:active { + cursor: pointer; +} + td.jqplot-table-legend > div { - border:1px solid #cccccc; - padding:0.2em; + border: 1px solid #cccccc; + padding:1px; } div.jqplot-table-legend-swatch { width:0px; height:0px; - border-top-width: 0.35em; - border-bottom-width: 0.35em; - border-left-width: 0.6em; - border-right-width: 0.6em; + border-top-width: 5px; + border-bottom-width: 5px; + border-left-width: 6px; + border-right-width: 6px; border-top-style: solid; border-bottom-style: solid; border-left-style: solid; @@ -156,6 +183,7 @@ table.jqplot-cursor-tooltip { .jqplot-point-label { font-size: 0.75em; + z-index: 2; } td.jqplot-cursor-legend-swatch { @@ -167,3 +195,32 @@ div.jqplot-cursor-legend-swatch { width:1.2em; height:0.7em; } + +.jqplot-error { +/* Styles added to the plot target container when there is an error go here.*/ + text-align: center; +} + +.jqplot-error-message { +/* Styling of the custom error message div goes here.*/ + position: relative; + top: 46%; + display: inline-block; +} + +div.jqplot-bubble-label { + font-size: 0.8em; +/* background: rgba(90%, 90%, 90%, 0.15);*/ + padding-left: 2px; + padding-right: 2px; + color: rgb(20%, 20%, 20%); +} + +div.jqplot-bubble-label.jqplot-bubble-label-highlight { + background: rgba(90%, 90%, 90%, 0.7); +} + +div.jqplot-noData-container { + text-align: center; + background-color: rgba(96%, 96%, 96%, 0.3); +} diff --git a/js/form.compressed.js b/js/form.compressed.js index 64ea3993ac..f4a7be5b96 100644 --- a/js/form.compressed.js +++ b/js/form.compressed.js @@ -121,7 +121,8 @@ if(this.docname){if(!this.check_doc_perm())return;if(!this.setup_done)this.setup if(this.doc.__islocal) this.is_editable[this.docname]=1;this.editable=this.is_editable[this.docname];if(!this.doc.__archived&&(this.editable||(!this.editable&&this.meta.istable))){if(this.print_wrapper){$dh(this.print_wrapper);$ds(this.page_layout.wrapper);} if(!this.meta.istable){this.refresh_header();this.sidebar&&this.sidebar.refresh();} -this.runclientscript('refresh');this.refresh_tabs();this.refresh_fields();this.refresh_dependency();this.refresh_footer();if(this.layout)this.layout.show();if(is_onload) +this.runclientscript('refresh');$(document).trigger('form_refresh') +this.refresh_tabs();this.refresh_fields();this.refresh_dependency();this.refresh_footer();if(this.layout)this.layout.show();if(is_onload) this.runclientscript('onload_post_render',this.doctype,this.docname);}else{this.refresh_header();if(this.print_wrapper){this.refresh_print_layout();} this.runclientscript('edit_status_changed');} if(!this.display)this.show_the_frm();if(!this.meta.in_dialog)page_body.change_to('Forms');}} @@ -256,18 +257,21 @@ _f.CodeField.prototype.init_editor=function(){var me=this;this.editor=tinymce.ge _f.CodeField.prototype.set_disp=function(val){$y(this.disp_area,{width:'90%'}) if(this.df.fieldtype=='Text Editor'){this.disp_area.innerHTML=val;}else{this.disp_area.innerHTML='';}} _f.cur_grid_cell=null;_f.Grid=function(parent){} -_f.Grid.prototype.init=function(parent,row_height){this.alt_row_bg='#F2F2FF';this.row_height=row_height;if(!row_height)this.row_height='26px';this.make_ui(parent);this.insert_column('','','Int','Sr','50px','',[1,0,0]);this.total_width=50;if(this.oninit)this.oninit();keypress_observers.push(this)} +_f.Grid.prototype.init=function(parent,row_height){this.col_idx_by_name={} +this.alt_row_bg='#F2F2FF';this.row_height=row_height;if(!row_height)this.row_height='26px';this.make_ui(parent);this.insert_column('','','Int','Sr','50px','',[1,0,0]);if(this.oninit)this.oninit();keypress_observers.push(this);} _f.Grid.prototype.make_ui=function(parent){var ht=make_table($a(parent,'div'),1,2,'100%',['60%','40%']);this.main_title=$td(ht,0,0);this.main_title.className='columnHeading';$td(ht,0,1).style.textAlign='right';this.tbar_div=$a($td(ht,0,1),'div','grid_tbarlinks');if(isIE)$y(this.tbar_div,{width:'200px'});this.tbar_tab=make_table(this.tbar_div,1,4,'100%',['25%','25%','25%','25%']);this.wrapper=$a(parent,'div','grid_wrapper');$h(this.wrapper,cint(screen.width*0.5)+'px');this.head_wrapper=$a(this.wrapper,'div','grid_head_wrapper');this.head_tab=$a(this.head_wrapper,'table','grid_head_table');this.head_row=this.head_tab.insertRow(0);this.tab_wrapper=$a(this.wrapper,'div','grid_tab_wrapper');this.tab=$a(this.tab_wrapper,'table','grid_table');var me=this;this.wrapper.onscroll=function(){me.head_wrapper.style.top=me.wrapper.scrollTop+'px';}} _f.Grid.prototype.show=function(){if(this.can_add_rows){$ds(this.tbar_div);}else{$dh(this.tbar_div);} $ds(this.wrapper);} _f.Grid.prototype.hide=function(){$dh(this.wrapper);$dh(this.tbar_div);} -_f.Grid.prototype.insert_column=function(doctype,fieldname,fieldtype,label,width,options,perm,reqd){var idx=this.head_row.cells.length;if(!width)width='100px';var col=this.head_row.insertCell(idx);col.doctype=doctype;col.fieldname=fieldname;col.fieldtype=fieldtype;col.innerHTML='
'+label+'
';col.label=label;if(reqd) -col.childNodes[0].style.color="#D22";this.total_width+=cint(width);$w(col,width);col.orig_width=col.style.width;col.options=options;col.perm=perm;} -_f.Grid.prototype.set_column_disp=function(label,show){for(var i=0;i'+label+'';col.label=label;if(reqd) +col.childNodes[0].style.color="#D22";col.style.width=width;col.options=options;col.perm=perm;this.col_idx_by_name[fieldname]=idx;} +_f.Grid.prototype.reset_table_width=function(){var w=0;for(var i=0,len=this.head_row.cells.length;ithis.tab.rows.length) +_f.Grid.prototype.set_data=function(data){this.cell_deselect();this.reset_table_width();if(data.length>this.tab.rows.length) this.append_rows(data.length-this.tab.rows.length);if(data.length'+label+'';c.cur_label=label;break;}}} _f.FormGrid.prototype.refresh=function(){var docset=getchildren(this.doctype,this.field.frm.docname,this.field.df.fieldname,this.field.frm.doctype);var data=[];for(var i=0;i 0, fill to previous series data. + var idx = this.index; + if (data.length == 2) { + if (idx == 0) { + this.gridData = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), + xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], + [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)], + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] + ]; + } + else { + var psd = plot.series[idx-1].data; + this.gridData = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), + xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], + [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])], + [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), + xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), + xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] + ]; + } + } + else { + if (idx == 0) { + this.gridData = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), + xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], + [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)], + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] + ]; + } + else { + var psd = plot.series[idx-1].data; + this.gridData = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), + xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], + [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])], + [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), + xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), + xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] + ]; + } + } + }; + + // Method: makeGridData + // converts any arbitrary data values to grid coordinates and + // returns them. This method exists so that plugins can use a series' + // linerenderer to generate grid data points without overwriting the + // grid data associated with that series. + // Called with scope of a series. + $.jqplot.BezierCurveRenderer.prototype.makeGridData = function(data, plot) { + // recalculate the grid data + var xp = this._xaxis.series_u2p; + var yp = this._yaxis.series_u2p; + var gd = []; + var pgd = []; + // if seriesIndex = 0, fill to x axis. + // if seriesIndex > 0, fill to previous series data. + var idx = this.index; + if (data.length == 2) { + if (idx == 0) { + gd = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), + xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], + [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)], + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] + ]; + } + else { + var psd = plot.series[idx-1].data; + gd = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), + xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], + [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])], + [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), + xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), + xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] + ]; + } + } + else { + if (idx == 0) { + gd = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), + xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], + [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)], + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] + ]; + } + else { + var psd = plot.series[idx-1].data; + gd = [ + [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], + [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), + xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), + xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], + [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])], + [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), + xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), + xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] + ]; + } + } + return gd; + }; + + + // called within scope of series. + $.jqplot.BezierCurveRenderer.prototype.draw = function(ctx, gd, options) { + var i; + ctx.save(); + if (gd.length) { + if (this.showLine) { + ctx.save(); + var opts = (options != null) ? options : {}; + ctx.fillStyle = opts.fillStyle || this.color; + ctx.beginPath(); + ctx.moveTo(gd[0][0], gd[0][1]); + ctx.bezierCurveTo(gd[1][0], gd[1][1], gd[1][2], gd[1][3], gd[1][4], gd[1][5]); + ctx.lineTo(gd[2][0], gd[2][1]); + if (gd[3].length == 2) { + ctx.lineTo(gd[3][0], gd[3][1]); + } + else { + ctx.bezierCurveTo(gd[3][0], gd[3][1], gd[3][2], gd[3][3], gd[3][4], gd[3][5]); + } + ctx.closePath(); + ctx.fill(); + ctx.restore(); + } + } + + ctx.restore(); + }; + + $.jqplot.BezierCurveRenderer.prototype.drawShadow = function(ctx, gd, options) { + // This is a no-op, shadows drawn with lines. + }; + + $.jqplot.BezierAxisRenderer = function() { + $.jqplot.LinearAxisRenderer.call(this); + }; + + $.jqplot.BezierAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); + $.jqplot.BezierAxisRenderer.prototype.constructor = $.jqplot.BezierAxisRenderer; + + + // Axes on a plot with Bezier Curves + $.jqplot.BezierAxisRenderer.prototype.init = function(options){ + $.extend(true, this, options); + var db = this._dataBounds; + // Go through all the series attached to this axis and find + // the min/max bounds for this axis. + for (var i=0; i db.max || db.max == null) { + db.max = d[j][0]; + } + } + else { + if (d[j][1] < db.min || db.min == null) { + db.min = d[j][1]; + } + if (d[j][1] > db.max || db.max == null) { + db.max = d[j][1]; + } + } + } + } + else { + if (this.name == 'xaxis' || this.name == 'x2axis') { + if (d[0][0] < db.min || db.min == null) { + db.min = d[0][0]; + } + if (d[0][0] > db.max || db.max == null) { + db.max = d[0][0]; + } + for (var j=0; j<5; j+=2) { + if (d[1][j] < db.min || db.min == null) { + db.min = d[1][j]; + } + if (d[1][j] > db.max || db.max == null) { + db.max = d[1][j]; + } + } + } + else { + if (d[0][1] < db.min || db.min == null) { + db.min = d[0][1]; + } + if (d[0][1] > db.max || db.max == null) { + db.max = d[0][1]; + } + for (var j=1; j<6; j+=2) { + if (d[1][j] < db.min || db.min == null) { + db.min = d[1][j]; + } + if (d[1][j] > db.max || db.max == null) { + db.max = d[1][j]; + } + } + } + } + } + }; + + // setup default renderers for axes and legend so user doesn't have to + // called with scope of plot + function preInit(target, data, options) { + options = options || {}; + options.axesDefaults = $.extend(true, {pad:0}, options.axesDefaults); + options.legend = $.extend(true, {placement:'outside'}, options.legend); + // only set these if there is a pie series + var setopts = false; + if (options.seriesDefaults.renderer == $.jqplot.BezierCurveRenderer) { + setopts = true; + } + else if (options.series) { + for (var i=0; i < options.series.length; i++) { + if (options.series[i].renderer == $.jqplot.BezierCurveRenderer) { + setopts = true; + } + } + } + + if (setopts) { + options.axesDefaults.renderer = $.jqplot.BezierAxisRenderer; + } + } + + $.jqplot.preInitHooks.push(preInit); + +})(jQuery); \ No newline at end of file diff --git a/js/jquery/jqplot-plugins/jqplot.BezierCurveRenderer.min.js b/js/jquery/jqplot-plugins/jqplot.BezierCurveRenderer.min.js new file mode 100644 index 0000000000..246b747952 --- /dev/null +++ b/js/jquery/jqplot-plugins/jqplot.BezierCurveRenderer.min.js @@ -0,0 +1,30 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.0b2_r792 + * + * Copyright (c) 2009-2011 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function(b){b.jqplot.BezierCurveRenderer=function(){b.jqplot.LineRenderer.call(this)};b.jqplot.BezierCurveRenderer.prototype=new b.jqplot.LineRenderer();b.jqplot.BezierCurveRenderer.prototype.constructor=b.jqplot.BezierCurveRenderer;b.jqplot.BezierCurveRenderer.prototype.setGridData=function(h){var e=this._xaxis.series_u2p;var g=this._yaxis.series_u2p;var f=this.data;this.gridData=[];this._prevGridData=[];var d=this.index;if(f.length==2){if(d==0){this.gridData=[[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,f[0][1])],[e.call(this._xaxis,f[1][0]),g.call(this._yaxis,f[1][1]),e.call(this._xaxis,f[1][2]),g.call(this._yaxis,f[1][3]),e.call(this._xaxis,f[1][4]),g.call(this._yaxis,f[1][5])],[e.call(this._xaxis,f[1][4]),g.call(this._yaxis,this._yaxis.min)],[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,this._yaxis.min)]]}else{var c=h.series[d-1].data;this.gridData=[[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,f[0][1])],[e.call(this._xaxis,f[1][0]),g.call(this._yaxis,f[1][1]),e.call(this._xaxis,f[1][2]),g.call(this._yaxis,f[1][3]),e.call(this._xaxis,f[1][4]),g.call(this._yaxis,f[1][5])],[e.call(this._xaxis,c[1][4]),g.call(this._yaxis,c[1][5])],[e.call(this._xaxis,c[1][2]),g.call(this._yaxis,c[1][3]),e.call(this._xaxis,c[1][0]),g.call(this._yaxis,c[1][1]),e.call(this._xaxis,c[0][0]),g.call(this._yaxis,c[0][1])]]}}else{if(d==0){this.gridData=[[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,f[0][1])],[e.call(this._xaxis,f[1][0]),g.call(this._yaxis,f[1][1]),e.call(this._xaxis,f[2][0]),g.call(this._yaxis,f[2][1]),e.call(this._xaxis,f[3][0]),g.call(this._yaxis,f[3][1])],[e.call(this._xaxis,f[3][1]),g.call(this._yaxis,this._yaxis.min)],[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,this._yaxis.min)]]}else{var c=h.series[d-1].data;this.gridData=[[e.call(this._xaxis,f[0][0]),g.call(this._yaxis,f[0][1])],[e.call(this._xaxis,f[1][0]),g.call(this._yaxis,f[1][1]),e.call(this._xaxis,f[2][0]),g.call(this._yaxis,f[2][1]),e.call(this._xaxis,f[3][0]),g.call(this._yaxis,f[3][1])],[e.call(this._xaxis,c[3][0]),g.call(this._yaxis,c[3][1])],[e.call(this._xaxis,c[2][0]),g.call(this._yaxis,c[2][1]),e.call(this._xaxis,c[1][0]),g.call(this._yaxis,c[1][1]),e.call(this._xaxis,c[0][0]),g.call(this._yaxis,c[0][1])]]}}};b.jqplot.BezierCurveRenderer.prototype.makeGridData=function(g,i){var f=this._xaxis.series_u2p;var h=this._yaxis.series_u2p;var e=[];var j=[];var d=this.index;if(g.length==2){if(d==0){e=[[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,g[0][1])],[f.call(this._xaxis,g[1][0]),h.call(this._yaxis,g[1][1]),f.call(this._xaxis,g[1][2]),h.call(this._yaxis,g[1][3]),f.call(this._xaxis,g[1][4]),h.call(this._yaxis,g[1][5])],[f.call(this._xaxis,g[1][4]),h.call(this._yaxis,this._yaxis.min)],[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,this._yaxis.min)]]}else{var c=i.series[d-1].data;e=[[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,g[0][1])],[f.call(this._xaxis,g[1][0]),h.call(this._yaxis,g[1][1]),f.call(this._xaxis,g[1][2]),h.call(this._yaxis,g[1][3]),f.call(this._xaxis,g[1][4]),h.call(this._yaxis,g[1][5])],[f.call(this._xaxis,c[1][4]),h.call(this._yaxis,c[1][5])],[f.call(this._xaxis,c[1][2]),h.call(this._yaxis,c[1][3]),f.call(this._xaxis,c[1][0]),h.call(this._yaxis,c[1][1]),f.call(this._xaxis,c[0][0]),h.call(this._yaxis,c[0][1])]]}}else{if(d==0){e=[[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,g[0][1])],[f.call(this._xaxis,g[1][0]),h.call(this._yaxis,g[1][1]),f.call(this._xaxis,g[2][0]),h.call(this._yaxis,g[2][1]),f.call(this._xaxis,g[3][0]),h.call(this._yaxis,g[3][1])],[f.call(this._xaxis,g[3][1]),h.call(this._yaxis,this._yaxis.min)],[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,this._yaxis.min)]]}else{var c=i.series[d-1].data;e=[[f.call(this._xaxis,g[0][0]),h.call(this._yaxis,g[0][1])],[f.call(this._xaxis,g[1][0]),h.call(this._yaxis,g[1][1]),f.call(this._xaxis,g[2][0]),h.call(this._yaxis,g[2][1]),f.call(this._xaxis,g[3][0]),h.call(this._yaxis,g[3][1])],[f.call(this._xaxis,c[3][0]),h.call(this._yaxis,c[3][1])],[f.call(this._xaxis,c[2][0]),h.call(this._yaxis,c[2][1]),f.call(this._xaxis,c[1][0]),h.call(this._yaxis,c[1][1]),f.call(this._xaxis,c[0][0]),h.call(this._yaxis,c[0][1])]]}}return e};b.jqplot.BezierCurveRenderer.prototype.draw=function(c,g,d){var e;c.save();if(g.length){if(this.showLine){c.save();var f=(d!=null)?d:{};c.fillStyle=f.fillStyle||this.color;c.beginPath();c.moveTo(g[0][0],g[0][1]);c.bezierCurveTo(g[1][0],g[1][1],g[1][2],g[1][3],g[1][4],g[1][5]);c.lineTo(g[2][0],g[2][1]);if(g[3].length==2){c.lineTo(g[3][0],g[3][1])}else{c.bezierCurveTo(g[3][0],g[3][1],g[3][2],g[3][3],g[3][4],g[3][5])}c.closePath();c.fill();c.restore()}}c.restore()};b.jqplot.BezierCurveRenderer.prototype.drawShadow=function(c,e,d){};b.jqplot.BezierAxisRenderer=function(){b.jqplot.LinearAxisRenderer.call(this)};b.jqplot.BezierAxisRenderer.prototype=new b.jqplot.LinearAxisRenderer();b.jqplot.BezierAxisRenderer.prototype.constructor=b.jqplot.BezierAxisRenderer;b.jqplot.BezierAxisRenderer.prototype.init=function(f){b.extend(true,this,f);var c=this._dataBounds;for(var g=0;gc.max||c.max==null){c.max=k[e][0]}}else{if(k[e][1]c.max||c.max==null){c.max=k[e][1]}}}}else{if(this.name=="xaxis"||this.name=="x2axis"){if(k[0][0]c.max||c.max==null){c.max=k[0][0]}for(var e=0;e<5;e+=2){if(k[1][e]c.max||c.max==null){c.max=k[1][e]}}}else{if(k[0][1]c.max||c.max==null){c.max=k[0][1]}for(var e=1;e<6;e+=2){if(k[1][e]c.max||c.max==null){c.max=k[1][e]}}}}}};function a(g,f,d){d=d||{};d.axesDefaults=b.extend(true,{pad:0},d.axesDefaults);d.legend=b.extend(true,{placement:"outside"},d.legend);var c=false;if(d.seriesDefaults.renderer==b.jqplot.BezierCurveRenderer){c=true}else{if(d.series){for(var e=0;e 1) { + this.breakOnNull = true; + var l = this.data.length; + var skip = parseInt(l/this.rendererOptions.groups, 10); + var count = 0; + for (var i=skip; i 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); + newrgb[j] = parseInt(newrgb[j], 10); + } + ret.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); + } + return ret; + } $.jqplot.BarRenderer.prototype.draw = function(ctx, gridData, options) { var i; - var opts = (options != undefined) ? options : {}; + // Ughhh, have to make a copy of options b/c it may be modified later. + var opts = $.extend({}, options); var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; var fill = (opts.fill != undefined) ? opts.fill : this.fill; @@ -178,16 +268,20 @@ var yaxis = this.yaxis; var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; - var pointx, pointy, nvals, nseries, pos; + var pointx, pointy; + // clear out data colors. + this._dataColors = []; + this._barPoints = []; if (this.barWidth == null) { this.renderer.setBarWidth.call(this); } - var temp = this.renderer.calcSeriesNumbers.call(this); - nvals = temp[0]; - nseries = temp[1]; - pos = temp[2]; + var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); + var nvals = temp[0]; + var nseries = temp[1]; + var pos = temp[2]; + var points = []; if (this._stack) { this._barNudge = 0; @@ -203,12 +297,18 @@ negativeColor = opts.fillStyle; } var positiveColor = opts.fillStyle; + var base; + var xstart; + var ystart; if (this.barDirection == 'vertical') { for (var i=0; i 0 && i < this.gridData.length-1) { ystart = this.gridData[i-1][1]; } + else if (this.waterfall && i == 0 && i < this.gridData.length-1) { + if (this._yaxis.min <= 0 && this._yaxis.max >= 0) { + ystart = this._yaxis.series_u2p(0); + } + else if (this._yaxis.min > 0) { + ystart = ctx.canvas.height; + } + else { + ystart = 0; + } + } + else if (this.waterfall && i == this.gridData.length - 1) { + if (this._yaxis.min <= 0 && this._yaxis.max >= 0) { + ystart = this._yaxis.series_u2p(0); + } + else if (this._yaxis.min > 0) { + ystart = ctx.canvas.height; + } + else { + ystart = 0; + } + } else { ystart = ctx.canvas.height; } } if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) { - if (this.varyBarColor) { + if (this.varyBarColor && !this._stack) { if (this.useNegativeColors) { opts.fillStyle = negativeColors.next(); } @@ -240,32 +362,50 @@ } } else { - if (this.varyBarColor) { + if (this.varyBarColor && !this._stack) { opts.fillStyle = positiveColors.next(); } else { opts.fillStyle = positiveColor; } } - - points.push([base-this.barWidth/2, ystart]); - points.push([base-this.barWidth/2, gridData[i][1]]); - points.push([base+this.barWidth/2, gridData[i][1]]); - points.push([base+this.barWidth/2, ystart]); + + if (!this.fillToZero || this._plotData[i][1] >= 0) { + points.push([base-this.barWidth/2, ystart]); + points.push([base-this.barWidth/2, gridData[i][1]]); + points.push([base+this.barWidth/2, gridData[i][1]]); + points.push([base+this.barWidth/2, ystart]); + } + // for negative bars make sure points are always ordered clockwise + else { + points.push([base-this.barWidth/2, gridData[i][1]]); + points.push([base-this.barWidth/2, ystart]); + points.push([base+this.barWidth/2, ystart]); + points.push([base+this.barWidth/2, gridData[i][1]]); + } + this._barPoints.push(points); // now draw the shadows if not stacked. // for stacked plots, they are predrawn by drawShadow if (shadow && !this._stack) { - this.renderer.shadowRenderer.draw(ctx, points, opts); + var sopts = $.extend(true, {}, opts); + // need to get rid of fillStyle on shadow. + delete sopts.fillStyle; + this.renderer.shadowRenderer.draw(ctx, points, sopts); } + var clr = opts.fillStyle || this.color; + this._dataColors.push(clr); this.renderer.shapeRenderer.draw(ctx, points, opts); } } else if (this.barDirection == 'horizontal'){ for (var i=0; i 0 && i < this.gridData.length-1) { xstart = this.gridData[i-1][1]; } + else if (this.waterfall && i == 0 && i < this.gridData.length-1) { + if (this._xaxis.min <= 0 && this._xaxis.max >= 0) { + xstart = this._xaxis.series_u2p(0); + } + else if (this._xaxis.min > 0) { + xstart = 0; + } + else { + xstart = ctx.canvas.width; + } + } + else if (this.waterfall && i == this.gridData.length - 1) { + if (this._xaxis.min <= 0 && this._xaxis.max >= 0) { + xstart = this._xaxis.series_u2p(0); + } + else if (this._xaxis.min > 0) { + xstart = 0; + } + else { + xstart = ctx.canvas.width; + } + } else { xstart = 0; } } if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) { - if (this.varyBarColor) { + if (this.varyBarColor && !this._stack) { if (this.useNegativeColors) { opts.fillStyle = negativeColors.next(); } @@ -293,7 +455,7 @@ } } else { - if (this.varyBarColor) { + if (this.varyBarColor && !this._stack) { opts.fillStyle = positiveColors.next(); } else { @@ -302,19 +464,36 @@ } points.push([xstart, base+this.barWidth/2]); - points.push([gridData[i][0], base+this.barWidth/2]); - points.push([gridData[i][0], base-this.barWidth/2]); points.push([xstart, base-this.barWidth/2]); + points.push([gridData[i][0], base-this.barWidth/2]); + points.push([gridData[i][0], base+this.barWidth/2]); + this._barPoints.push(points); // now draw the shadows if not stacked. // for stacked plots, they are predrawn by drawShadow if (shadow && !this._stack) { - this.renderer.shadowRenderer.draw(ctx, points, opts); + var sopts = $.extend(true, {}, opts); + delete sopts.fillStyle; + this.renderer.shadowRenderer.draw(ctx, points, sopts); } + var clr = opts.fillStyle || this.color; + this._dataColors.push(clr); this.renderer.shapeRenderer.draw(ctx, points, opts); } } } - + + if (this.highlightColors.length == 0) { + this.highlightColors = computeHighlightColors(this._dataColors); + } + + else if (typeof(this.highlightColors) == 'string') { + var temp = this.highlightColors; + this.highlightColors = []; + for (var i=0; i0){this.data[d][j]+=this.data[d-1][j]}}this.data[this.data.length]=(j==1)?[this.data.length+1,e]:[e,this.data.length+1];this._data[this._data.length]=(j==1)?[this._data.length+1,e]:[e,this._data.length+1]}}b.jqplot.preSeriesInitHooks.push(a);b.jqplot.BarRenderer.prototype.calcSeriesNumbers=function(){var g=0;var h=0;var f=this[this._primaryAxis];var e,d,j;for(var c=0;c0&&u0&&u0){this.data[p][t]+=this.data[p-1][t]}}this.data[this.data.length]=(t==1)?[this.data.length+1,r]:[r,this.data.length+1];this._data[this._data.length]=(t==1)?[this._data.length+1,r]:[r,this._data.length+1]}if(this.rendererOptions.groups>1){this.breakOnNull=true;var m=this.data.length;var u=parseInt(m/this.rendererOptions.groups,10);var q=0;for(var p=u;p570)?m[o]*0.8:m[o]+0.3*(255-m[o]);m[o]=parseInt(m[o],10)}p.push("rgb("+m[0]+","+m[1]+","+m[2]+")")}return p}d.jqplot.BarRenderer.prototype.draw=function(D,J,p){var G;var z=d.extend({},p);var u=(z.shadow!=undefined)?z.shadow:this.shadow;var M=(z.showLine!=undefined)?z.showLine:this.showLine;var E=(z.fill!=undefined)?z.fill:this.fill;var o=this.xaxis;var H=this.yaxis;var x=this._xaxis.series_u2p;var I=this._yaxis.series_u2p;var C,B;this._dataColors=[];this._barPoints=[];if(this.barWidth==null){this.renderer.setBarWidth.call(this)}var L=this._plotSeriesInfo=this.renderer.calcSeriesNumbers.call(this);var w=L[0];var v=L[1];var r=L[2];var F=[];if(this._stack){this._barNudge=0}else{this._barNudge=(-Math.abs(v/2-0.5)+r)*(this.barWidth+this.barPadding)}if(M){var t=new d.jqplot.ColorGenerator(this.negativeSeriesColors);var A=new d.jqplot.ColorGenerator(this.seriesColors);var K=t.get(this.index);if(!this.useNegativeColors){K=z.fillStyle}var s=z.fillStyle;var q;var N;var n;if(this.barDirection=="vertical"){for(var G=0;G0&&G=0){n=this._yaxis.series_u2p(0)}else{if(this._yaxis.min>0){n=D.canvas.height}else{n=0}}}else{if(this.waterfall&&G==this.gridData.length-1){if(this._yaxis.min<=0&&this._yaxis.max>=0){n=this._yaxis.series_u2p(0)}else{if(this._yaxis.min>0){n=D.canvas.height}else{n=0}}}else{n=D.canvas.height}}}}}if((this.fillToZero&&this._plotData[G][1]<0)||(this.waterfall&&this._data[G][1]<0)){if(this.varyBarColor&&!this._stack){if(this.useNegativeColors){z.fillStyle=t.next()}else{z.fillStyle=A.next()}}else{z.fillStyle=K}}else{if(this.varyBarColor&&!this._stack){z.fillStyle=A.next()}else{z.fillStyle=s}}if(!this.fillToZero||this._plotData[G][1]>=0){F.push([q-this.barWidth/2,n]);F.push([q-this.barWidth/2,J[G][1]]);F.push([q+this.barWidth/2,J[G][1]]);F.push([q+this.barWidth/2,n])}else{F.push([q-this.barWidth/2,J[G][1]]);F.push([q-this.barWidth/2,n]);F.push([q+this.barWidth/2,n]);F.push([q+this.barWidth/2,J[G][1]])}this._barPoints.push(F);if(u&&!this._stack){var y=d.extend(true,{},z);delete y.fillStyle;this.renderer.shadowRenderer.draw(D,F,y)}var m=z.fillStyle||this.color;this._dataColors.push(m);this.renderer.shapeRenderer.draw(D,F,z)}}else{if(this.barDirection=="horizontal"){for(var G=0;G0&&G=0){N=this._xaxis.series_u2p(0)}else{if(this._xaxis.min>0){N=0}else{N=D.canvas.width}}}else{if(this.waterfall&&G==this.gridData.length-1){if(this._xaxis.min<=0&&this._xaxis.max>=0){N=this._xaxis.series_u2p(0)}else{if(this._xaxis.min>0){N=0}else{N=D.canvas.width}}}else{N=0}}}}}if((this.fillToZero&&this._plotData[G][1]<0)||(this.waterfall&&this._data[G][1]<0)){if(this.varyBarColor&&!this._stack){if(this.useNegativeColors){z.fillStyle=t.next()}else{z.fillStyle=A.next()}}}else{if(this.varyBarColor&&!this._stack){z.fillStyle=A.next()}else{z.fillStyle=s}}F.push([N,q+this.barWidth/2]);F.push([N,q-this.barWidth/2]);F.push([J[G][0],q-this.barWidth/2]);F.push([J[G][0],q+this.barWidth/2]);this._barPoints.push(F);if(u&&!this._stack){var y=d.extend(true,{},z);delete y.fillStyle;this.renderer.shadowRenderer.draw(D,F,y)}var m=z.fillStyle||this.color;this._dataColors.push(m);this.renderer.shapeRenderer.draw(D,F,z)}}}}if(this.highlightColors.length==0){this.highlightColors=f(this._dataColors)}else{if(typeof(this.highlightColors)=="string"){var L=this.highlightColors;this.highlightColors=[];for(var G=0;G [[x1, y1, "label 1", {css}], [x2, y2, "label 2", {css}], ...] + * + * The label and css object are optional. If the label is ommitted, the + * box will collapse unless a css height and/or width is specified. + * + * The css object is an object specifying css properties + * such as: + * + * > {background:'#4f98a5', border:'3px solid gray', padding:'1px'} + * + * Note that css properties specified with the data point override defaults + * specified with the series. + * + */ + $.jqplot.BlockRenderer = function(){ + $.jqplot.LineRenderer.call(this); + }; + + $.jqplot.BlockRenderer.prototype = new $.jqplot.LineRenderer(); + $.jqplot.BlockRenderer.prototype.constructor = $.jqplot.BlockRenderer; + + // called with scope of a series + $.jqplot.BlockRenderer.prototype.init = function(options) { + // Group: Properties + // + // prop: css + // default css styles that will be applied to all data blocks. + // these values will be overridden by css styles supplied with the + // individulal data points. + this.css = {padding:'2px', border:'1px solid #999', textAlign:'center'}; + // prop: escapeHtml + // true to escape html in the box label. + this.escapeHtml = false; + // prop: insertBreaks + // true to turn spaces in data block label into html breaks
. + this.insertBreaks = true; + // prop: varyBlockColors + // true to vary the color of each block in this series according to + // the seriesColors array. False to set each block to the color + // specified on this series. This has no effect if a css background color + // option is specified in the renderer css options. + this.varyBlockColors = false; + $.extend(true, this, options); + if (this.css.backgroundColor) { + this.color = this.css.backgroundColor; + } + else if (this.css.background) { + this.color = this.css.background; + } + else if (!this.varyBlockColors) { + this.css.background = this.color; + } + this.canvas = new $.jqplot.BlockCanvas(); + this.shadowCanvas = new $.jqplot.BlockCanvas(); + this.canvas._plotDimensions = this._plotDimensions; + this.shadowCanvas._plotDimensions = this._plotDimensions; + this._type = 'block'; + + // group: Methods + // + // Method: moveBlock + // Moves an individual block. More efficient than redrawing + // the whole series by calling plot.drawSeries(). + // Properties: + // idx - the 0 based index of the block or point in this series. + // x - the x coordinate in data units (value on x axis) to move the block to. + // y - the y coordinate in data units (value on the y axis) to move the block to. + // duration - optional parameter to create an animated movement. Can be a + // number (higher is slower animation) or 'fast', 'normal' or 'slow'. If not + // provided, the element is moved without any animation. + this.moveBlock = function (idx, x, y, duration) { + // update plotData, stackData, data and gridData + // x and y are in data coordinates. + var el = this.canvas._elem.children(':eq('+idx+')'); + this.data[idx][0] = x; + this.data[idx][1] = y; + this._plotData[idx][0] = x; + this._plotData[idx][1] = y; + this._stackData[idx][0] = x; + this._stackData[idx][1] = y; + this.gridData[idx][0] = this._xaxis.series_u2p(x); + this.gridData[idx][1] = this._yaxis.series_u2p(y); + var w = el.outerWidth(); + var h = el.outerHeight(); + var left = this.gridData[idx][0] - w/2 + 'px'; + var top = this.gridData[idx][1] - h/2 + 'px'; + if (duration) { + if (parseInt(duration, 10)) { + duration = parseInt(duration, 10); + } + el.animate({left:left, top:top}, duration); + } + else { + el.css({left:left, top:top}); + } + el = null; + }; + }; + + // called with scope of series + $.jqplot.BlockRenderer.prototype.draw = function (ctx, gd, options) { + if (this.plugins.pointLabels) { + this.plugins.pointLabels.show = false; + } + var i, el, d, gd, t, css, w, h, left, top; + var opts = (options != undefined) ? options : {}; + var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); + this.canvas._elem.empty(); + for (i=0; i'); + } + css = $.extend(true, {}, this.css, css); + // create a div + el = $('
'); + this.canvas._elem.append(el); + // set text + this.escapeHtml ? el.text(t) : el.html(t); + // style it + // remove styles we don't want overridden. + delete css.position; + delete css.marginRight; + delete css.marginLeft; + if (!css.background && !css.backgroundColor && !css.backgroundImage){ + css.background = colorGenerator.next(); + } + el.css(css); + w = el.outerWidth(); + h = el.outerHeight(); + left = gd[0] - w/2 + 'px'; + top = gd[1] - h/2 + 'px'; + el.css({left:left, top:top}); + el = null; + } + }; + + $.jqplot.BlockCanvas = function() { + $.jqplot.ElemContainer.call(this); + this._ctx; + }; + + $.jqplot.BlockCanvas.prototype = new $.jqplot.ElemContainer(); + $.jqplot.BlockCanvas.prototype.constructor = $.jqplot.BlockCanvas; + + $.jqplot.BlockCanvas.prototype.createElement = function(offsets, clss, plotDimensions) { + this._offsets = offsets; + var klass = 'jqplot-blockCanvas'; + if (clss != undefined) { + klass = clss; + } + var elem; + // if this canvas already has a dom element, don't make a new one. + if (this._elem) { + elem = this._elem.get(0); + } + else { + elem = document.createElement('div'); + } + // if new plotDimensions supplied, use them. + if (plotDimensions != undefined) { + this._plotDimensions = plotDimensions; + } + + var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px'; + var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px'; + this._elem = $(elem); + this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top }); + + this._elem.addClass(klass); + return this._elem; + }; + + $.jqplot.BlockCanvas.prototype.setContext = function() { + this._ctx = { + canvas:{ + width:0, + height:0 + }, + clearRect:function(){return null;} + }; + return this._ctx; + }; + +})(jQuery); + + \ No newline at end of file diff --git a/js/jquery/jqplot-plugins/jqplot.blockRenderer.min.js b/js/jquery/jqplot-plugins/jqplot.blockRenderer.min.js new file mode 100644 index 0000000000..62404280b9 --- /dev/null +++ b/js/jquery/jqplot-plugins/jqplot.blockRenderer.min.js @@ -0,0 +1,30 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.0b2_r792 + * + * Copyright (c) 2009-2011 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function(a){a.jqplot.BlockRenderer=function(){a.jqplot.LineRenderer.call(this)};a.jqplot.BlockRenderer.prototype=new a.jqplot.LineRenderer();a.jqplot.BlockRenderer.prototype.constructor=a.jqplot.BlockRenderer;a.jqplot.BlockRenderer.prototype.init=function(b){this.css={padding:"2px",border:"1px solid #999",textAlign:"center"};this.escapeHtml=false;this.insertBreaks=true;this.varyBlockColors=false;a.extend(true,this,b);if(this.css.backgroundColor){this.color=this.css.backgroundColor}else{if(this.css.background){this.color=this.css.background}else{if(!this.varyBlockColors){this.css.background=this.color}}}this.canvas=new a.jqplot.BlockCanvas();this.shadowCanvas=new a.jqplot.BlockCanvas();this.canvas._plotDimensions=this._plotDimensions;this.shadowCanvas._plotDimensions=this._plotDimensions;this._type="block";this.moveBlock=function(l,j,i,e){var c=this.canvas._elem.children(":eq("+l+")");this.data[l][0]=j;this.data[l][1]=i;this._plotData[l][0]=j;this._plotData[l][1]=i;this._stackData[l][0]=j;this._stackData[l][1]=i;this.gridData[l][0]=this._xaxis.series_u2p(j);this.gridData[l][1]=this._yaxis.series_u2p(i);var k=c.outerWidth();var f=c.outerHeight();var d=this.gridData[l][0]-k/2+"px";var g=this.gridData[l][1]-f/2+"px";if(e){if(parseInt(e,10)){e=parseInt(e,10)}c.animate({left:d,top:g},e)}else{c.css({left:d,top:g})}c=null}};a.jqplot.BlockRenderer.prototype.draw=function(q,o,r){if(this.plugins.pointLabels){this.plugins.pointLabels.show=false}var f,c,l,o,p,k,n,g,e,m;var b=(r!=undefined)?r:{};var j=new a.jqplot.ColorGenerator(this.seriesColors);this.canvas._elem.empty();for(f=0;f")}k=a.extend(true,{},this.css,k);c=a('
');this.canvas._elem.append(c);this.escapeHtml?c.text(p):c.html(p);delete k.position;delete k.marginRight;delete k.marginLeft;if(!k.background&&!k.backgroundColor&&!k.backgroundImage){k.background=j.next()}c.css(k);n=c.outerWidth();g=c.outerHeight();e=o[0]-n/2+"px";m=o[1]-g/2+"px";c.css({left:e,top:m});c=null}};a.jqplot.BlockCanvas=function(){a.jqplot.ElemContainer.call(this);this._ctx};a.jqplot.BlockCanvas.prototype=new a.jqplot.ElemContainer();a.jqplot.BlockCanvas.prototype.constructor=a.jqplot.BlockCanvas;a.jqplot.BlockCanvas.prototype.createElement=function(i,e,c){this._offsets=i;var b="jqplot-blockCanvas";if(e!=undefined){b=e}var g;if(this._elem){g=this._elem.get(0)}else{g=document.createElement("div")}if(c!=undefined){this._plotDimensions=c}var d=this._plotDimensions.width-this._offsets.left-this._offsets.right+"px";var f=this._plotDimensions.height-this._offsets.top-this._offsets.bottom+"px";this._elem=a(g);this._elem.css({position:"absolute",width:d,height:f,left:this._offsets.left,top:this._offsets.top});this._elem.addClass(b);return this._elem};a.jqplot.BlockCanvas.prototype.setContext=function(){this._ctx={canvas:{width:0,height:0},clearRect:function(){return null}};return this._ctx}})(jQuery); \ No newline at end of file diff --git a/js/jquery/jqplot-plugins/jqplot.bubbleRenderer.js b/js/jquery/jqplot-plugins/jqplot.bubbleRenderer.js new file mode 100644 index 0000000000..2d70ace224 --- /dev/null +++ b/js/jquery/jqplot-plugins/jqplot.bubbleRenderer.js @@ -0,0 +1,754 @@ +/** + * jqPlot + * Pure JavaScript plotting plugin using jQuery + * + * Version: 1.0.0b2_r792 + * + * Copyright (c) 2009-2011 Chris Leonello + * jqPlot is currently available for use in all personal or commercial projects + * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL + * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can + * choose the license that best suits your project and use it accordingly. + * + * Although not required, the author would appreciate an email letting him + * know of any substantial use of jqPlot. You can reach the author at: + * chris at jqplot dot com or see http://www.jqplot.com/info.php . + * + * If you are feeling kind and generous, consider supporting the project by + * making a donation at: http://www.jqplot.com/donate.php . + * + * sprintf functions contained in jqplot.sprintf.js by Ash Searle: + * + * version 2007.04.27 + * author Ash Searle + * http://hexmen.com/blog/2007/03/printf-sprintf/ + * http://hexmen.com/js/sprintf.js + * The author (Ash Searle) has placed this code in the public domain: + * "This code is unrestricted: you are free to use it however you like." + * + */ +(function($) { + var arrayMax = function( array ){ + return Math.max.apply( Math, array ); + }; + var arrayMin = function( array ){ + return Math.min.apply( Math, array ); + }; + + /** + * Class: $.jqplot.BubbleRenderer + * Plugin renderer to draw a bubble chart. A Bubble chart has data points displayed as + * colored circles with an optional text label inside. To use + * the bubble renderer, you must include the bubble renderer like: + * + * > + * + * Data must be supplied in + * the form: + * + * > [[x1, y1, r1,