diff --git a/core/doctype/communication/communication.txt b/core/doctype/communication/communication.txt
index 9b38e9d31d..ceb4378e71 100644
--- a/core/doctype/communication/communication.txt
+++ b/core/doctype/communication/communication.txt
@@ -1,6 +1,6 @@
[
{
- "creation": "2013-01-10 16:34:00",
+ "creation": "2013-01-28 17:06:59",
"docstatus": 0,
"modified": "2013-01-28 15:29:13",
"modified_by": "Administrator",
@@ -26,14 +26,11 @@
"permlevel": 0
},
{
- "cancel": 1,
- "create": 1,
"doctype": "DocPerm",
"name": "__common__",
"parent": "Communication",
"parentfield": "permissions",
"parenttype": "DocType",
- "permlevel": 0,
"read": 1,
"report": 1,
"submit": 0,
@@ -243,16 +240,6 @@
"fieldtype": "Date",
"label": "Date"
},
- {
- "doctype": "DocField",
- "fieldname": "file_list",
- "fieldtype": "Text",
- "hidden": 1,
- "in_list_view": 1,
- "label": "File List",
- "no_copy": 1,
- "print_hide": 1
- },
{
"doctype": "DocField",
"fieldname": "_user_tags",
@@ -263,26 +250,53 @@
"print_hide": 1
},
{
- "amend": 0,
+ "create": 1,
"doctype": "DocPerm",
+ "permlevel": 0,
"role": "Support Team"
},
{
- "amend": 0,
+ "create": 1,
"doctype": "DocPerm",
+ "permlevel": 0,
+ "role": "Sales Manager"
+ },
+ {
+ "create": 1,
+ "doctype": "DocPerm",
+ "permlevel": 0,
+ "role": "Sales User"
+ },
+ {
+ "amend": 0,
+ "cancel": 0,
+ "create": 0,
+ "doctype": "DocPerm",
+ "match": "",
+ "permlevel": 1,
"role": "Sales Manager"
},
{
"amend": 0,
+ "cancel": 0,
+ "create": 0,
"doctype": "DocPerm",
- "role": "Sales User"
- },
- {
- "doctype": "DocPerm",
+ "match": "",
+ "permlevel": 1,
"role": "Support Manager"
},
{
+ "cancel": 1,
+ "create": 1,
"doctype": "DocPerm",
+ "permlevel": 0,
+ "role": "Support Manager"
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "doctype": "DocPerm",
+ "permlevel": 0,
"role": "System Manager"
}
]
\ No newline at end of file
diff --git a/core/doctype/doctype/doctype.py b/core/doctype/doctype/doctype.py
index 065f0b872c..b0a1aebfbd 100644
--- a/core/doctype/doctype/doctype.py
+++ b/core/doctype/doctype/doctype.py
@@ -40,7 +40,8 @@ class DocType:
sql('UPDATE tabDocType SET modified="%s" WHERE `name`="%s"' % (now(), p[0]))
def scrub_field_names(self):
- restricted = ('name','parent','idx','owner','creation','modified','modified_by','parentfield','parenttype')
+ restricted = ('name','parent','idx','owner','creation','modified','modified_by',
+ 'parentfield','parenttype',"file_list")
for d in self.doclist:
if d.parent and d.fieldtype:
if (not d.fieldname):
@@ -80,7 +81,6 @@ class DocType:
validate_permissions(self.doclist.get({"doctype":"DocPerm"}))
self.set_version()
self.make_amendable()
- self.make_file_list()
self.check_link_replacement_error()
def on_update(self):
@@ -139,24 +139,6 @@ class DocType:
"doctype_template.py"), 'r') as srcfile:
pyfile.write(srcfile.read())
- def make_file_list(self):
- """
- if allow_attach is checked and the column file_list doesn't exist,
- create a new field 'file_list'
- """
- if self.doc.allow_attach:
- if not webnotes.conn.sql("""select name from tabDocField
- where fieldname = 'file_list' and parent = %s""", self.doc.name):
- new = self.doc.addchild('fields', 'DocField', self.doclist)
- new.label = 'File List'
- new.fieldtype = 'Text'
- new.fieldname = 'file_list'
- new.hidden = 1
- new.permlevel = 0
- new.print_hide = 1
- new.no_copy = 1
- new.idx = self.get_max_idx() + 1
-
def make_amendable(self):
"""
if is_submittable is set, add amended_from docfields
diff --git a/core/doctype/file_data/file_data.py b/core/doctype/file_data/file_data.py
index 44a449c9e3..d1138a0b31 100644
--- a/core/doctype/file_data/file_data.py
+++ b/core/doctype/file_data/file_data.py
@@ -27,45 +27,27 @@ record of files
naming for same name files: file.gif, file-1.gif, file-2.gif etc
"""
-import webnotes
+import webnotes, webnotes.utils, os
class DocType():
def __init__(self, d, dl):
self.doc, self.doclist = d, dl
- def autoname(self):
- """save file by its name"""
- if not self.doc.file_name:
- raise Exception, 'file name missing'
-
- if not '.' in self.doc.file_name:
- raise Exception, 'file name must have extension (.)'
-
- self.doc.file_name = self.doc.file_name.replace('-', '')
-
- parts = self.doc.file_name.split('.')
-
- same = webnotes.conn.sql("""select name from `tabFile Data`
- where name=%s""", self.doc.file_name)
-
- if same:
- # check for more
- other_list = webnotes.conn.sql("""select name from `tabFile Data`
- where name like '%s-%%.%s'""" % (parts[0], '.'.join(parts[1:])))
+ def on_update(self):
+ # check duplicate assignement
+ n_records = webnotes.conn.sql("""select count(*) from `tabFile Data`
+ where file_name=%s
+ and attached_to_doctype=%s
+ and attached_to_name=%s""", (self.doc.file_name, self.doc.attached_to_doctype,
+ self.doc.attached_to_name))[0][0]
+ if n_records > 1:
+ webnotes.msgprint(webnotes._("Same file has already been attached to the record"))
+ raise webnotes.DuplicateEntryError
- if other_list:
- from webnotes.utils import cint
- # gets the max number from format like name-###.ext
- last_num = max(
- (cint(other[0].split('.')[0].split('-')[-1])
- for other in other_list)
- )
- else:
- last_num = 0
-
- new_id = "%03d" % (last_num + 1)
-
- # new name
- self.doc.file_name = parts[0] + '-' + new_id + '.' + '.'.join(parts[1:])
-
- self.doc.name = self.doc.file_name
\ No newline at end of file
+ def on_trash(self):
+ if webnotes.conn.sql("""select count(*) from `tabFile Data`
+ where file_name=%s""", self.doc.file_name)[0][0]==1:
+ path = webnotes.utils.get_path("public", "files", self.doc.file_name)
+ if os.path.exists(path):
+ os.remove(path)
+
\ No newline at end of file
diff --git a/core/doctype/file_data/file_data.txt b/core/doctype/file_data/file_data.txt
index 37ea5f6e9d..35cfd07a9e 100644
--- a/core/doctype/file_data/file_data.txt
+++ b/core/doctype/file_data/file_data.txt
@@ -1,51 +1,83 @@
[
{
- "owner": "Administrator",
+ "creation": "2012-12-12 11:19:22",
"docstatus": 0,
- "creation": "2012-11-30 18:13:34",
+ "modified": "2013-04-10 13:34:29",
"modified_by": "Administrator",
- "modified": "2012-12-11 14:56:34"
+ "owner": "Administrator"
},
{
- "read_only": 0,
- "autoname": "FileData/.#####",
- "name": "__common__",
+ "autoname": "File.######",
"doctype": "DocType",
- "module": "Core"
+ "module": "Core",
+ "name": "__common__",
+ "read_only": 0
},
{
+ "doctype": "DocField",
"name": "__common__",
"parent": "File Data",
- "doctype": "DocField",
+ "parentfield": "fields",
"parenttype": "DocType",
"permlevel": 0,
- "parentfield": "fields"
+ "read_only": 1
},
{
- "name": "File Data",
- "doctype": "DocType"
+ "cancel": 1,
+ "doctype": "DocPerm",
+ "name": "__common__",
+ "parent": "File Data",
+ "parentfield": "permissions",
+ "parenttype": "DocType",
+ "permlevel": 0,
+ "read": 1,
+ "role": "System Manager",
+ "write": 1
+ },
+ {
+ "doctype": "DocType",
+ "name": "File Data"
},
{
- "oldfieldtype": "Data",
"doctype": "DocField",
+ "fieldname": "file_name",
+ "fieldtype": "Data",
"label": "File Name",
"oldfieldname": "file_name",
- "fieldname": "file_name",
- "fieldtype": "Data"
+ "oldfieldtype": "Data"
},
{
"doctype": "DocField",
- "label": "File URL",
"fieldname": "file_url",
- "fieldtype": "Data"
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "File URL"
},
{
- "oldfieldtype": "Link",
"doctype": "DocField",
- "label": "Module",
- "oldfieldname": "module",
- "fieldname": "module",
+ "fieldname": "attached_to_doctype",
"fieldtype": "Link",
- "options": "Module Def"
+ "in_list_view": 1,
+ "label": "Attached To DocType",
+ "options": "DocType",
+ "search_index": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "attached_to_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Attached To Name",
+ "search_index": 1
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "file_size",
+ "fieldtype": "Int",
+ "in_list_view": 1,
+ "label": "File Size"
+ },
+ {
+ "doctype": "DocPerm"
}
]
\ No newline at end of file
diff --git a/core/doctype/letter_head/letter_head.js b/core/doctype/letter_head/letter_head.js
index 013aff084a..60086ee016 100644
--- a/core/doctype/letter_head/letter_head.js
+++ b/core/doctype/letter_head/letter_head.js
@@ -57,8 +57,10 @@ cur_frm.cscript['set_from_image'] = function(doc, dt, dn) {
if(!confirm('Are you sure you want to overwrite the existing HTML?'))
return;
}
+
+
- var file_name = doc.file_list.split(',')[1]
+ var file_name = cur_frm.get_files()[0];
if(!in_list(['gif','jpg','jpeg','png'], file_name.split('.')[1].toLowerCase())) {
msgprint("Please upload a web friendly (GIF, JPG or PNG) image file for the letter head");
diff --git a/core/doctype/letter_head/letter_head.txt b/core/doctype/letter_head/letter_head.txt
index 817dcd2f7c..31183b6dbe 100644
--- a/core/doctype/letter_head/letter_head.txt
+++ b/core/doctype/letter_head/letter_head.txt
@@ -1,21 +1,18 @@
[
{
- "creation": "2012-11-19 12:06:53",
+ "creation": "2012-11-22 17:45:46",
"docstatus": 0,
"modified": "2012-11-21 17:39:17",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
- "_last_update": "1307340319",
"allow_attach": 1,
"autoname": "field:letter_head_name",
"doctype": "DocType",
"max_attachments": 3,
"module": "Core",
- "name": "__common__",
- "section_style": "Simple",
- "version": 1
+ "name": "__common__"
},
{
"doctype": "DocField",
@@ -26,6 +23,7 @@
"permlevel": 0
},
{
+ "cancel": 1,
"create": 1,
"doctype": "DocPerm",
"name": "__common__",
@@ -34,7 +32,9 @@
"parenttype": "DocType",
"permlevel": 0,
"read": 1,
+ "report": 1,
"role": "System Manager",
+ "submit": 0,
"write": 1
},
{
@@ -83,8 +83,7 @@
"doctype": "DocField",
"fieldname": "set_from_image",
"fieldtype": "Button",
- "label": "Set From Image",
- "trigger": "Client"
+ "label": "Set From Image"
},
{
"depends_on": "letter_head_name",
@@ -103,15 +102,6 @@
"hidden": 1,
"label": "URL"
},
- {
- "doctype": "DocField",
- "fieldname": "file_list",
- "fieldtype": "Text",
- "hidden": 1,
- "label": "File LIst",
- "oldfieldname": "file_list",
- "oldfieldtype": "Text"
- },
{
"doctype": "DocPerm"
}
diff --git a/core/doctype/profile/profile.py b/core/doctype/profile/profile.py
index b1334a7873..10bdd86a8b 100644
--- a/core/doctype/profile/profile.py
+++ b/core/doctype/profile/profile.py
@@ -187,7 +187,10 @@ Thank you,
'product': startup.product_name,
'user_fullname': get_user_fullname(webnotes.session['user'])
}
- sendmail_md(self.doc.email, subject=subject, msg=txt % args)
+
+ sender = webnotes.session.user != "Administrator" and webnotes.session.user or None
+
+ sendmail_md(recipients=self.doc.email, sender=sender, subject=subject, msg=txt % args)
def on_trash(self):
if self.doc.name in ["Administrator", "Guest"]:
diff --git a/core/doctype/profile/profile.txt b/core/doctype/profile/profile.txt
index f4ca204eec..e8b53df461 100644
--- a/core/doctype/profile/profile.txt
+++ b/core/doctype/profile/profile.txt
@@ -1,6 +1,6 @@
[
{
- "creation": "2013-02-14 17:37:36",
+ "creation": "2013-03-07 12:26:21",
"docstatus": 0,
"modified": "2013-03-01 10:18:03",
"modified_by": "Administrator",
@@ -387,16 +387,6 @@
"oldfieldtype": "Read Only",
"read_only": 1
},
- {
- "doctype": "DocField",
- "fieldname": "file_list",
- "fieldtype": "Text",
- "hidden": 1,
- "label": "File List",
- "no_copy": 1,
- "oldfieldname": "file_list",
- "oldfieldtype": "Text"
- },
{
"doctype": "DocField",
"fieldname": "roles_assigned_to_user",
diff --git a/core/doctype/report/report.txt b/core/doctype/report/report.txt
index 98e91bb12f..cc1dbdd052 100644
--- a/core/doctype/report/report.txt
+++ b/core/doctype/report/report.txt
@@ -1,8 +1,8 @@
[
{
- "creation": "2013-02-25 13:11:50",
+ "creation": "2013-03-09 15:45:57",
"docstatus": 0,
- "modified": "2013-02-25 14:18:36",
+ "modified": "2013-04-30 17:50:00",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -41,6 +41,7 @@
"fieldname": "report_name",
"fieldtype": "Data",
"label": "Report Name",
+ "read_only": 0,
"reqd": 1
},
{
@@ -50,6 +51,7 @@
"in_list_view": 1,
"label": "Ref DocType",
"options": "DocType",
+ "read_only": 0,
"reqd": 1
},
{
@@ -59,12 +61,20 @@
"in_list_view": 1,
"label": "Is Standard",
"options": "No\nYes",
+ "read_only": 0,
"reqd": 1
},
+ {
+ "doctype": "DocField",
+ "fieldname": "add_total_row",
+ "fieldtype": "Check",
+ "label": "Add Total Row"
+ },
{
"doctype": "DocField",
"fieldname": "column_break_4",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "read_only": 0
},
{
"doctype": "DocField",
@@ -72,25 +82,29 @@
"fieldtype": "Select",
"label": "Report Type",
"options": "Report Builder\nQuery Report\nScript Report",
+ "read_only": 0,
"reqd": 1
},
{
"doctype": "DocField",
"fieldname": "disabled",
"fieldtype": "Check",
- "label": "Disabled"
+ "label": "Disabled",
+ "read_only": 0
},
{
"doctype": "DocField",
"fieldname": "section_break_6",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "read_only": 0
},
{
"depends_on": "eval:doc.report_type==\"Query Report\"",
"doctype": "DocField",
"fieldname": "query",
"fieldtype": "Code",
- "label": "Query"
+ "label": "Query",
+ "read_only": 0
},
{
"depends_on": "eval:doc.report_type==\"Report Builder\"",
diff --git a/core/doctype/system_console/system_console.js b/core/doctype/system_console/system_console.js
index 462082017f..f9a89c4d30 100644
--- a/core/doctype/system_console/system_console.js
+++ b/core/doctype/system_console/system_console.js
@@ -26,7 +26,7 @@ cur_frm.cscript['server_python'] = function(doc, dt, dn) {
$c_obj(make_doclist(doc.doctype, doc.name), 'execute_server', '', function(r, rt) {
doc = locals[doc.doctype][doc.name];
if(r.exc) {
- doc.response = r.exc;
+ doc.response = (r.exc || []).join("\n");
} else {
doc.response = 'Worked!'.bold()
}
diff --git a/core/page/data_import_tool/data_import_tool.js b/core/page/data_import_tool/data_import_tool.js
index 580fbaaa1f..eb13aaab3d 100644
--- a/core/page/data_import_tool/data_import_tool.js
+++ b/core/page/data_import_tool/data_import_tool.js
@@ -141,7 +141,7 @@ wn.pages['data-import-tool'].onload = function(wrapper) {
});
// add overwrite option
- var $submit_btn = $('#dit-upload-area form input[type="submit"]');
+ var $submit_btn = $('#dit-upload-area input[type="submit"]');
$('\
Overwrite\
If you are uploading a child table (for example Item Price), the all the entries of that table will be deleted (for that parent record) and new entries will be made.
").appendTo(this.input_area).get(0);
-
- this.input = {};
- this.myid = wn.dom.set_unique_id(this.pre);
- this.editor = ace.edit(this.myid);
-
- if(me.df.options=='Markdown' || me.df.options=='HTML') {
- wn.require('lib/js/lib/ace/mode-html.js');
- var HTMLMode = require("ace/mode/html").Mode;
- me.editor.getSession().setMode(new HTMLMode());
- }
-
- else if(me.df.options=='Javascript') {
- wn.require('lib/js/lib/ace/mode-javascript.js');
- var JavascriptMode = require("ace/mode/javascript").Mode;
- me.editor.getSession().setMode(new JavascriptMode());
- }
-
- else if(me.df.options=='Python') {
- wn.require('lib/js/lib/ace/mode-python.js');
- var PythonMode = require("ace/mode/python").Mode;
- me.editor.getSession().setMode(new PythonMode());
- }
-
- this.input.set_input = function(v) {
- // during field refresh in run trigger, set_input is called
- // if called during on_change, setting doesn't make sense
- // and causes cursor to shift back to first position
- if(me.changing_value) return;
-
- me.setting_value = true;
- me.editor.getSession().setValue(v==null ? "" : v);
- me.setting_value = false;
- }
-
- this.get_value = function() {
- return me.editor.getSession().getValue(); // tinyMCE
- }
- $(cur_frm.wrapper).bind('render_complete', function() {
- me.editor.resize();
- me.editor.getSession().on('change', function() {
- if(me.setting_value) return;
- var val = me.get_value();
- if(locals[cur_frm.doctype][cur_frm.docname][me.df.fieldname] != val) {
- me.set(me.get_value());
-
- me.changing_value = true;
- me.run_trigger();
- me.changing_value = false;
- }
- })
+ this.input = new wn.editors.ACE({
+ parent: this.input_area,
+ change: function(value) {
+ me.set_value_and_run_trigger(value);
+ },
+ field: this
});
- this.onrefresh = function() {
- me.editor && me.editor.resize();
- }
}
-
+ this.get_value = function() {
+ return this.input.get_value();
+ }
}
-_f.CodeField.prototype.init_editor = function() {
- // attach onchange methods
- var me = this;
- this.editor = tinymce.get(this.myid);
- this.editor.onKeyUp.add(function(ed, e) {
- me.set(ed.getContent());
- });
- this.editor.onPaste.add(function(ed, e) {
- me.set(ed.getContent());
- });
- this.editor.onSetContent.add(function(ed, e) {
- me.set(ed.getContent());
- });
-
- // reset content
- var c = locals[cur_frm.doctype][cur_frm.docname][this.df.fieldname];
- if(cur_frm && c) {
- this.editor.setContent(c);
+_f.CodeField.prototype.set_value_and_run_trigger = function(value) {
+ if(locals[cur_frm.doctype][cur_frm.docname][this.df.fieldname] != value) {
+ this.set(value);
+ this.changing_value = true;
+ this.run_trigger();
+ this.changing_value = false;
}
}
@@ -363,7 +256,8 @@ _f.CodeField.prototype.set_disp = function(val) {
if(this.df.fieldtype=='Text Editor') {
this.disp_area.innerHTML = val;
} else {
- this.disp_area.innerHTML = '';
+ this.disp_area.innerHTML = '';
}
}
diff --git a/public/js/lib/beautify-html.js b/public/js/lib/beautify-html.js
new file mode 100644
index 0000000000..1d6270071c
--- /dev/null
+++ b/public/js/lib/beautify-html.js
@@ -0,0 +1,617 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+ The MIT License (MIT)
+
+ Copyright (c) 2007-2013 Einar Lielmanis and contributors.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+
+ Style HTML
+---------------
+
+ Written by Nochum Sossonko, (nsossonko@hotmail.com)
+
+ Based on code initially developed by: Einar Lielmanis,
+ http://jsbeautifier.org/
+
+ Usage:
+ style_html(html_source);
+
+ style_html(html_source, options);
+
+ The options are:
+ indent_size (default 4) — indentation size,
+ indent_char (default space) — character to indent with,
+ max_char (default 250) - maximum amount of characters per line (0 = disable)
+ brace_style (default "collapse") - "collapse" | "expand" | "end-expand"
+ put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.
+ unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted
+ indent_scripts (default normal) - "keep"|"separate"|"normal"
+
+ e.g.
+
+ style_html(html_source, {
+ 'indent_size': 2,
+ 'indent_char': ' ',
+ 'max_char': 78,
+ 'brace_style': 'expand',
+ 'unformatted': ['a', 'sub', 'sup', 'b', 'i', 'u']
+ });
+*/
+
+(function() {
+
+ function style_html(html_source, options, js_beautify, css_beautify) {
+ //Wrapper function to invoke all the necessary constructors and deal with the output.
+
+ var multi_parser,
+ indent_size,
+ indent_character,
+ max_char,
+ brace_style,
+ unformatted;
+
+ options = options || {};
+ indent_size = options.indent_size || 4;
+ indent_character = options.indent_char || ' ';
+ brace_style = options.brace_style || 'collapse';
+ max_char = options.max_char === 0 ? Infinity : options.max_char || 250;
+ unformatted = options.unformatted || ['a', 'span', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike', 'font', 'ins', 'del', 'pre', 'address', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
+
+ function Parser() {
+
+ this.pos = 0; //Parser position
+ this.token = '';
+ this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
+ this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
+ parent: 'parent1',
+ parentcount: 1,
+ parent1: ''
+ };
+ this.tag_type = '';
+ this.token_text = this.last_token = this.last_text = this.token_type = '';
+
+ this.Utils = { //Uilities made available to the various functions
+ whitespace: "\n\r\t ".split(''),
+ single_token: 'br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed,?php,?,?='.split(','), //all the single tags for HTML
+ extra_liners: 'head,body,/html'.split(','), //for tags that need a line of whitespace before them
+ in_array: function (what, arr) {
+ for (var i=0; i= this.input.length) {
+ return content.length?content.join(''):['', 'TK_EOF'];
+ }
+
+ input_char = this.input.charAt(this.pos);
+ this.pos++;
+ this.line_char_count++;
+
+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
+ if (content.length) {
+ space = true;
+ }
+ this.line_char_count--;
+ continue; //don't want to insert unnecessary space
+ }
+ else if (space) {
+ if (this.line_char_count >= this.max_char) { //insert a line when the max_char is reached
+ content.push('\n');
+ for (var i=0; i', 'igm');
+ reg_match.lastIndex = this.pos;
+ var reg_array = reg_match.exec(this.input);
+ var end_script = reg_array?reg_array.index:this.input.length; //absolute end of script
+ if(this.pos < end_script) { //get everything in between the script tags
+ content = this.input.substring(this.pos, end_script);
+ this.pos = end_script;
+ }
+ return content;
+ };
+
+ this.record_tag = function (tag){ //function to record a tag and its parent in this.tags Object
+ if (this.tags[tag + 'count']) { //check for the existence of this tag type
+ this.tags[tag + 'count']++;
+ this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
+ }
+ else { //otherwise initialize this tag type
+ this.tags[tag + 'count'] = 1;
+ this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
+ }
+ this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
+ this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
+ };
+
+ this.retrieve_tag = function (tag) { //function to retrieve the opening tag to the corresponding closer
+ if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
+ var temp_parent = this.tags.parent; //check to see if it's a closable tag.
+ while (temp_parent) { //till we reach '' (the initial value);
+ if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it
+ break;
+ }
+ temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
+ }
+ if (temp_parent) { //if we caught something
+ this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
+ this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
+ }
+ delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
+ delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
+ if (this.tags[tag + 'count'] === 1) {
+ delete this.tags[tag + 'count'];
+ }
+ else {
+ this.tags[tag + 'count']--;
+ }
+ }
+ };
+
+ this.get_tag = function (peek) { //function to get a full tag and parse its type
+ var input_char = '',
+ content = [],
+ comment = '',
+ space = false,
+ tag_start, tag_end,
+ orig_pos = this.pos,
+ orig_line_char_count = this.line_char_count;
+
+ peek = peek !== undefined ? peek : false;
+
+ do {
+ if (this.pos >= this.input.length) {
+ if (peek) {
+ this.pos = orig_pos;
+ this.line_char_count = orig_line_char_count;
+ }
+ return content.length?content.join(''):['', 'TK_EOF'];
+ }
+
+ input_char = this.input.charAt(this.pos);
+ this.pos++;
+ this.line_char_count++;
+
+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
+ space = true;
+ this.line_char_count--;
+ continue;
+ }
+
+ if (input_char === "'" || input_char === '"') {
+ if (!content[1] || content[1] !== '!') { //if we're in a comment strings don't get treated specially
+ input_char += this.get_unformatted(input_char);
+ space = true;
+ }
+ }
+
+ if (input_char === '=') { //no space before =
+ space = false;
+ }
+
+ if (content.length && content[content.length-1] !== '=' && input_char !== '>' && space) {
+ //no space after = or before >
+ if (this.line_char_count >= this.max_char) {
+ this.print_newline(false, content);
+ this.line_char_count = 0;
+ }
+ else {
+ content.push(' ');
+ this.line_char_count++;
+ }
+ space = false;
+ }
+ if (input_char === '<') {
+ tag_start = this.pos - 1;
+ }
+ content.push(input_char); //inserts character at-a-time (or string)
+ } while (input_char !== '>');
+
+ var tag_complete = content.join('');
+ var tag_index;
+ if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends
+ tag_index = tag_complete.indexOf(' ');
+ }
+ else { //otherwise go with the tag ending
+ tag_index = tag_complete.indexOf('>');
+ }
+ var tag_check = tag_complete.substring(1, tag_index).toLowerCase();
+ if (tag_complete.charAt(tag_complete.length-2) === '/' ||
+ this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
+ if ( ! peek) {
+ this.tag_type = 'SINGLE';
+ }
+ }
+ else if (tag_check === 'script') { //for later script handling
+ if ( ! peek) {
+ this.record_tag(tag_check);
+ this.tag_type = 'SCRIPT';
+ }
+ }
+ else if (tag_check === 'style') { //for future style handling (for now it justs uses get_content)
+ if ( ! peek) {
+ this.record_tag(tag_check);
+ this.tag_type = 'STYLE';
+ }
+ }
+ else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags
+ comment = this.get_unformatted(''+tag_check+'>', tag_complete); //...delegate to get_unformatted function
+ content.push(comment);
+ // Preserve collapsed whitespace either before or after this tag.
+ if (tag_start > 0 && this.Utils.in_array(this.input.charAt(tag_start - 1), this.Utils.whitespace)){
+ content.splice(0, 0, this.input.charAt(tag_start - 1));
+ }
+ tag_end = this.pos - 1;
+ if (this.Utils.in_array(this.input.charAt(tag_end + 1), this.Utils.whitespace)){
+ content.push(this.input.charAt(tag_end + 1));
+ }
+ this.tag_type = 'SINGLE';
+ }
+ else if (tag_check.charAt(0) === '!') { //peek for so...
+ comment = this.get_unformatted('-->', tag_complete); //...delegate to get_unformatted
+ content.push(comment);
+ }
+ if ( ! peek) {
+ this.tag_type = 'START';
+ }
+ }
+ else if (tag_check.indexOf('[endif') !== -1) {//peek for ', tag_complete);
+ content.push(comment);
+ this.tag_type = 'SINGLE';
+ }
+ }
+ else if ( ! peek) {
+ if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending
+ this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
+ this.tag_type = 'END';
+ }
+ else { //otherwise it's a start-tag
+ this.record_tag(tag_check); //push it on the tag stack
+ this.tag_type = 'START';
+ }
+ if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
+ this.print_newline(true, this.output);
+ }
+ }
+
+ if (peek) {
+ this.pos = orig_pos;
+ this.line_char_count = orig_line_char_count;
+ }
+
+ return content.join(''); //returns fully formatted tag
+ };
+
+ this.get_unformatted = function (delimiter, orig_tag) { //function to return unformatted content in its entirety
+
+ if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) {
+ return '';
+ }
+ var input_char = '';
+ var content = '';
+ var space = true;
+ do {
+
+ if (this.pos >= this.input.length) {
+ return content;
+ }
+
+ input_char = this.input.charAt(this.pos);
+ this.pos++;
+
+ if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
+ if (!space) {
+ this.line_char_count--;
+ continue;
+ }
+ if (input_char === '\n' || input_char === '\r') {
+ content += '\n';
+ /* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect tags if they are specified in the 'unformatted array'
+ for (var i=0; i]*>\s*$/);
+
+ // if next_tag comes back but is not an isolated tag, then
+ // let's treat the 'a' tag as having content
+ // and respect the unformatted option
+ if (!tag || this.Utils.in_array(tag, unformatted)){
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ this.printer = function (js_source, indent_character, indent_size, max_char, brace_style) { //handles input/output and some other printing functions
+
+ this.input = js_source || ''; //gets the input for the Parser
+ this.output = [];
+ this.indent_character = indent_character;
+ this.indent_string = '';
+ this.indent_size = indent_size;
+ this.brace_style = brace_style;
+ this.indent_level = 0;
+ this.max_char = max_char;
+ this.line_char_count = 0; //count to see if max_char was exceeded
+
+ for (var i=0; i 0) {
+ this.indent_level--;
+ }
+ };
+ };
+ return this;
+ }
+
+ /*_____________________--------------------_____________________*/
+
+ multi_parser = new Parser(); //wrapping functions Parser
+ multi_parser.printer(html_source, indent_character, indent_size, max_char, brace_style); //initialize starting values
+
+ while (true) {
+ var t = multi_parser.get_token();
+ multi_parser.token_text = t[0];
+ multi_parser.token_type = t[1];
+
+ if (multi_parser.token_type === 'TK_EOF') {
+ break;
+ }
+
+ switch (multi_parser.token_type) {
+ case 'TK_TAG_START':
+ multi_parser.print_newline(false, multi_parser.output);
+ multi_parser.print_token(multi_parser.token_text);
+ multi_parser.indent();
+ multi_parser.current_mode = 'CONTENT';
+ break;
+ case 'TK_TAG_STYLE':
+ case 'TK_TAG_SCRIPT':
+ multi_parser.print_newline(false, multi_parser.output);
+ multi_parser.print_token(multi_parser.token_text);
+ multi_parser.current_mode = 'CONTENT';
+ break;
+ case 'TK_TAG_END':
+ //Print new line only if the tag has no content and has child
+ if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
+ var tag_name = multi_parser.token_text.match(/\w+/)[0];
+ var tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length -1].match(/<\s*(\w+)/);
+ if (tag_extracted_from_last_output === null || tag_extracted_from_last_output[1] !== tag_name) {
+ multi_parser.print_newline(true, multi_parser.output);
+ }
+ }
+ multi_parser.print_token(multi_parser.token_text);
+ multi_parser.current_mode = 'CONTENT';
+ break;
+ case 'TK_TAG_SINGLE':
+ // Don't add a newline before elements that should remain unformatted.
+ var tag_check = multi_parser.token_text.match(/^\s*<([a-z]+)/i);
+ if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)){
+ multi_parser.print_newline(false, multi_parser.output);
+ }
+ multi_parser.print_token(multi_parser.token_text);
+ multi_parser.current_mode = 'CONTENT';
+ break;
+ case 'TK_CONTENT':
+ if (multi_parser.token_text !== '') {
+ multi_parser.print_token(multi_parser.token_text);
+ }
+ multi_parser.current_mode = 'TAG';
+ break;
+ case 'TK_STYLE':
+ case 'TK_SCRIPT':
+ if (multi_parser.token_text !== '') {
+ multi_parser.output.push('\n');
+ var text = multi_parser.token_text,
+ _beautifier,
+ script_indent_level = 1;
+ if (multi_parser.token_type === 'TK_SCRIPT') {
+ _beautifier = typeof js_beautify === 'function' && js_beautify;
+ } else if (multi_parser.token_type === 'TK_STYLE') {
+ _beautifier = typeof css_beautify === 'function' && css_beautify;
+ }
+
+ if (options.indent_scripts === "keep") {
+ script_indent_level = 0;
+ } else if (options.indent_scripts === "separate") {
+ script_indent_level = -multi_parser.indent_level;
+ }
+
+ var indentation = multi_parser.get_full_indent(script_indent_level);
+ if (_beautifier) {
+ // call the Beautifier if avaliable
+ text = _beautifier(text.replace(/^\s*/, indentation), options);
+ } else {
+ // simply indent the string otherwise
+ var white = text.match(/^\s*/)[0];
+ var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
+ var reindent = multi_parser.get_full_indent(script_indent_level -_level);
+ text = text.replace(/^\s*/, indentation)
+ .replace(/\r\n|\r|\n/g, '\n' + reindent)
+ .replace(/\s*$/, '');
+ }
+ if (text) {
+ multi_parser.print_token(text);
+ multi_parser.print_newline(true, multi_parser.output);
+ }
+ }
+ multi_parser.current_mode = 'TAG';
+ break;
+ }
+ multi_parser.last_token = multi_parser.token_type;
+ multi_parser.last_text = multi_parser.token_text;
+ }
+ return multi_parser.output.join('');
+ }
+
+ // If we're running a web page and don't have either of the above, add our one global
+ window.html_beautify = function(html_source, options) {
+ return style_html(html_source, options, window.js_beautify, window.css_beautify);
+ };
+
+}());
\ No newline at end of file
diff --git a/public/js/lib/bootstrap-wysiwyg.js b/public/js/lib/bootstrap-wysiwyg.js
new file mode 100644
index 0000000000..11da013f89
--- /dev/null
+++ b/public/js/lib/bootstrap-wysiwyg.js
@@ -0,0 +1,187 @@
+/* http://github.com/mindmup/bootstrap-wysiwyg */
+/*global jQuery, $, FileReader*/
+/*jslint browser:true*/
+jQuery(function ($) {
+ 'use strict';
+ var readFileIntoDataUrl = function (fileInfo) {
+ var loader = $.Deferred(),
+ fReader = new FileReader();
+
+ wn.upload.upload_file(fileInfo, {
+ from_form: 1,
+ doctype: cur_frm.doctype,
+ docname: cur_frm.docname
+ }, function(fileid, filename, r) {
+ if(!r.exc) {
+ if(fileid)
+ cur_frm.attachments.update_attachment(fileid, filename);
+ loader.resolve("files/" + filename);
+ }
+ });
+ return loader.promise();
+ };
+ $.fn.cleanHtml = function () {
+ var html = $(this).html();
+ return html && html.replace(/(
|\s|
<\/div>| )*$/, '');
+ };
+ $.fn.wysiwyg = function (userOptions) {
+ var editor = this,
+ selectedRange,
+ defaultOptions = {
+ hotKeys: {
+ 'ctrl+b meta+b': 'bold',
+ 'ctrl+i meta+i': 'italic',
+ 'ctrl+u meta+u': 'underline',
+ 'ctrl+z meta+z': 'undo',
+ 'ctrl+y meta+y meta+shift+z': 'redo',
+ 'ctrl+l meta+l': 'justifyleft',
+ 'ctrl+e meta+e': 'justifycenter',
+ 'ctrl+j meta+j': 'justifyfull',
+ 'shift+tab': 'outdent',
+ 'tab': 'indent'
+ },
+ toolbarSelector: '[data-role=editor-toolbar]',
+ commandRole: 'edit',
+ activeToolbarClass: 'btn-info',
+ selectionMarker: 'edit-focus-marker',
+ selectionColor: 'darkgrey'
+ },
+ options,
+ updateToolbar = function () {
+ if (options.activeToolbarClass) {
+ $(options.toolbarSelector).find('.btn[data-' + options.commandRole + ']').each(function () {
+ var command = $(this).data(options.commandRole);
+ if (document.queryCommandState(command)) {
+ $(this).addClass(options.activeToolbarClass);
+ } else {
+ $(this).removeClass(options.activeToolbarClass);
+ }
+ });
+ }
+ },
+ execCommand = function (commandWithArgs, valueArg) {
+ var commandArr = commandWithArgs.split(' '),
+ command = commandArr.shift(),
+ args = commandArr.join(' ') + (valueArg || '');
+ document.execCommand(command, 0, args);
+ updateToolbar();
+ },
+ bindHotkeys = function (hotKeys) {
+ $.each(hotKeys, function (hotkey, command) {
+ editor.keydown(hotkey, function (e) {
+ if (editor.attr('contenteditable') && editor.is(':visible')) {
+ e.preventDefault();
+ e.stopPropagation();
+ execCommand(command);
+ }
+ }).keyup(hotkey, function (e) {
+ if (editor.attr('contenteditable') && editor.is(':visible')) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ });
+ });
+ },
+ getCurrentRange = function () {
+ var sel = window.getSelection();
+ if (sel.getRangeAt && sel.rangeCount) {
+ return sel.getRangeAt(0);
+ }
+ },
+ saveSelection = function () {
+ selectedRange = getCurrentRange();
+ },
+ restoreSelection = function () {
+ var selection = window.getSelection();
+ if (selectedRange) {
+ selection.removeAllRanges();
+ selection.addRange(selectedRange);
+ }
+ },
+ insertFiles = function (files) {
+ editor.focus();
+ $.each(files, function (idx, fileInfo) {
+ if (/^image\//.test(fileInfo.type)) {
+ $.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
+ execCommand('insertimage', dataUrl);
+ });
+ }
+ });
+ },
+ markSelection = function (input, color) {
+ restoreSelection();
+ document.execCommand('hiliteColor', 0, color || 'transparent');
+ saveSelection();
+ input.data(options.selectionMarker, color);
+ },
+ bindToolbar = function (toolbar, options) {
+ toolbar.find('a[data-' + options.commandRole + ']').click(function () {
+ restoreSelection();
+ editor.focus();
+ execCommand($(this).data(options.commandRole));
+ saveSelection();
+ });
+ toolbar.find('[data-toggle=dropdown]').click(restoreSelection);
+
+ toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () {
+ var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */
+ this.value = '';
+ restoreSelection();
+ if (newValue) {
+ editor.focus();
+ execCommand($(this).data(options.commandRole), newValue);
+ }
+ saveSelection();
+ }).on('focus', function () {
+ var input = $(this);
+ if (!input.data(options.selectionMarker)) {
+ markSelection(input, options.selectionColor);
+ input.focus();
+ }
+ }).on('blur', function () {
+ var input = $(this);
+ if (input.data(options.selectionMarker)) {
+ markSelection(input, false);
+ }
+ });
+ toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () {
+ restoreSelection();
+ if (this.type === 'file' && this.files && this.files.length > 0) {
+ insertFiles(this.files);
+ }
+ saveSelection();
+ this.value = '';
+ });
+ },
+ initFileDrops = function () {
+ editor.on('dragenter dragover', false)
+ .on('drop', function (e) {
+ var dataTransfer = e.originalEvent.dataTransfer;
+ e.stopPropagation();
+ e.preventDefault();
+ if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
+ insertFiles(dataTransfer.files);
+ }
+ });
+ };
+ options = $.extend({}, defaultOptions, userOptions);
+ bindHotkeys(options.hotKeys);
+ initFileDrops();
+ bindToolbar($(options.toolbarSelector), options);
+ editor.attr('contenteditable', true)
+ .on('mouseup keyup mouseout', function () {
+ saveSelection();
+ updateToolbar();
+ });
+ $(window).bind('touchend', function (e) {
+ var isInside = (editor.is(e.target) || editor.has(e.target).length > 0),
+ currentRange = getCurrentRange(),
+ clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset);
+ if (!clear || isInside) {
+ saveSelection();
+ updateToolbar();
+ }
+ });
+ return this;
+ };
+});
\ No newline at end of file
diff --git a/public/js/lib/jquery/jquery.hotkeys.js b/public/js/lib/jquery/jquery.hotkeys.js
new file mode 100644
index 0000000000..5905f9db2a
--- /dev/null
+++ b/public/js/lib/jquery/jquery.hotkeys.js
@@ -0,0 +1,100 @@
+/*
+ * jQuery Hotkeys Plugin
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ *
+ * Based upon the plugin by Tzury Bar Yochay:
+ * http://github.com/tzuryby/hotkeys
+ *
+ * Original idea by:
+ * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
+*/
+
+(function(jQuery){
+
+ jQuery.hotkeys = {
+ version: "0.8",
+
+ specialKeys: {
+ 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
+ 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
+ 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
+ 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
+ 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
+ 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
+ 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
+ },
+
+ shiftNums: {
+ "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
+ "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
+ ".": ">", "/": "?", "\\": "|"
+ }
+ };
+
+ function keyHandler( handleObj ) {
+ // Only care when a possible input has been specified
+ if ( typeof handleObj.data !== "string" ) {
+ return;
+ }
+
+ var origHandler = handleObj.handler,
+ keys = handleObj.data.toLowerCase().split(" "),
+ textAcceptingInputTypes = ["text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", "datetime-local", "search", "color"];
+
+ handleObj.handler = function( event ) {
+ // Don't fire in text-accepting inputs that we didn't directly bind to
+ if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
+ jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1 ) ) {
+ return;
+ }
+
+ // Keypress represents characters, not special keys
+ var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
+ character = String.fromCharCode( event.which ).toLowerCase(),
+ key, modif = "", possible = {};
+
+ // check combinations (alt|ctrl|shift+anything)
+ if ( event.altKey && special !== "alt" ) {
+ modif += "alt+";
+ }
+
+ if ( event.ctrlKey && special !== "ctrl" ) {
+ modif += "ctrl+";
+ }
+
+ // TODO: Need to make sure this works consistently across platforms
+ if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
+ modif += "meta+";
+ }
+
+ if ( event.shiftKey && special !== "shift" ) {
+ modif += "shift+";
+ }
+
+ if ( special ) {
+ possible[ modif + special ] = true;
+
+ } else {
+ possible[ modif + character ] = true;
+ possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
+
+ // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
+ if ( modif === "shift+" ) {
+ possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
+ }
+ }
+
+ for ( var i = 0, l = keys.length; i < l; i++ ) {
+ if ( possible[ keys[i] ] ) {
+ return origHandler.apply( this, arguments );
+ }
+ }
+ };
+ }
+
+ jQuery.each([ "keydown", "keyup", "keypress" ], function() {
+ jQuery.event.special[ this ] = { add: keyHandler };
+ });
+
+})( jQuery );
\ No newline at end of file
diff --git a/public/js/wn/form/attachments.js b/public/js/wn/form/attachments.js
index 734393293d..cb9788ac82 100644
--- a/public/js/wn/form/attachments.js
+++ b/public/js/wn/form/attachments.js
@@ -41,7 +41,7 @@ wn.ui.form.Attachments = Class.extend({
},
max_reached: function() {
// no of attachments
- var n = this.frm.doc.file_list ? this.frm.doc.file_list.split('\n').length : 0;
+ var n = keys(this.get_file_list()).length;
// button if the number of attachments is less than max
if(n < this.frm.meta.max_attachments || !this.frm.meta.max_attachments) {
@@ -50,7 +50,8 @@ wn.ui.form.Attachments = Class.extend({
return true;
},
refresh: function() {
- if(this.frm.doc.__islocal || !this.frm.meta.allow_attach) {
+ var doc = this.frm.doc;
+ if(doc.__islocal || !this.frm.meta.allow_attach) {
this.parent.toggle(false);
return;
}
@@ -59,20 +60,19 @@ wn.ui.form.Attachments = Class.extend({
this.$list.empty();
- var fl = this.get_filelist();
+ var file_list = this.get_file_list();
+ var file_names = keys(file_list).sort();
// add attachment objects
- for(var i=0; i\
+ \
+ \
+ \
+ \
+ \
+ \
+ ').appendTo(this.opts.parent);
+ this.$parent = $(this.opts.parent);
+ this.$editor = $("#" + this.myid)
+ this.$textarea = this.$parent.find(".html-editor");
+ this.input = this.$editor.get(0);
+ },
+ make_bindings: function() {
+ var me = this;
+ var fonts = ['Serif', 'Sans', 'Arial', 'Arial Black', 'Courier',
+ 'Courier New', 'Comic Sans MS', 'Helvetica', 'Impact', 'Lucida Grande',
+ 'Lucida Sans', 'Tahoma', 'Times', 'Times New Roman', 'Verdana'],
+ fontTarget = this.$parent.find('[title=Font]').siblings('.dropdown-menu');
+
+ $.each(fonts, function (idx, fontName) {
+ fontTarget.append($(''+
+ fontName + ' '));
+ });
+
+ //this.$parent.find('a[title]').tooltip({container:'body'});
+
+ this.$parent.find('.dropdown-menu input').click(function() {return false;})
+ .change(function () {
+ $(this).parent('.dropdown-menu').siblings('.dropdown-toggle')
+ .dropdown('toggle');
+ })
+ .keydown('esc', function () {
+ this.value='';$(this).change();
+ });
+
+ // magic-overlay
+ this.$parent.find('[data-role=magic-overlay]').each(function () {
+ var overlay = $(this), target = $(overlay.data('target'));
+ overlay.css('opacity', 0).css('position', 'absolute')
+ .offset(target.offset())
+ .width(40).height(30);
+ });
+
+ this.$editor
+ .wysiwyg()
+ .on("mouseup keyup mouseout", function() {
+ var value = $(this).html();
+ if(value==null) value="";
+ me.opts.change(value);
+ })
+ this.$textarea
+ .on("change", function() {
+ var value = $(this).val();
+ if(value==null) value="";
+ me.opts.change(value);
+ });
+
+ this.current_editor = this.$editor;
+ this.$parent.find(".btn-html").click(function() {
+ if($(this).attr("disabled")=="disabled") return;
+ wn.require("lib/js/lib/beautify-html.js");
+ me.$textarea.val(html_beautify(me.$editor.cleanHtml()));
+ me.$parent.find(".for-rich-text").toggle(false);
+ me.$parent.find(".for-html").toggle(true);
+ me.$parent.find(".btn-html").addClass("btn-info").attr("disabled", "disabled");
+ me.$parent.find(".btn-rich-text").removeClass("btn-info").attr("disabled", false);
+ me.current_editor = me.$textarea;
+ });
+
+ this.$parent.find(".btn-rich-text").click(function() {
+ if($(this).attr("disabled")=="disabled") return;
+ me.$editor.html(me.$textarea.val());
+ me.$parent.find(".for-rich-text").toggle(true);
+ me.$parent.find(".for-html").toggle(false);
+ me.$parent.find(".btn-html").removeClass("btn-info").attr("disabled", false);
+ me.$parent.find(".btn-rich-text").addClass("btn-info").attr("disabled", "disabled");
+ me.current_editor = me.$editor;
+ });
+
+ },
+ set_input: function(value) {
+ if(this.value!=value) {
+ this.value = value==null ? "" : value;
+ this.$editor.html(this.value);
+ this.$textarea.val(this.value);
+ }
+ },
+ get_value: function() {
+ if(this.current_editor==this.$editor)
+ return this.$editor.cleanHtml();
+ else
+ return this.$textarea.val();
+ }
+})
+
+//// TinyMCE
+
+wn.editors.TinyMCE = Class.extend({
+ init: function(opts) {
+ this.opts = opts;
+ this.make();
+ },
+ make: function() {
+ var me = this;
+ this.input = $("