diff --git a/bower.json b/bower.json
index 4214bac..ddbc409 100644
--- a/bower.json
+++ b/bower.json
@@ -18,7 +18,8 @@
"dependencies": {
"angular": "~1.x",
"jquery": "2.x",
- "jquery-ui": "latest"
+ "jquery-ui": "latest",
+ "jison":"git://github.com/zaach/jison.git"
},
"devDependencies": {
"angular-mocks": "~1.x"
diff --git a/lexersrc/README.md b/lexersrc/README.md
new file mode 100644
index 0000000..2e840f4
--- /dev/null
+++ b/lexersrc/README.md
@@ -0,0 +1,10 @@
+These files generate the Parser.js found in the main source of this repo. The parser was generated using a tool called jison which can be found here. Currently the primary purpose of the parser is to add the ability to parse json arguments to the drag and drop callbacks.
+
+##Changing the parser.
+
+If you need to make changes to the parser you can update the parser.lex and the parser.y files then use jison to generate the Parser.js file.The command should look like this:
+
+node_modules/.bin/jison lexersrc/parser.y lexersrc/parser.lex --outfile src/Parser.js
+
+This will generate a javascript parsing engine in the src/Parser.js file, based on your two grammar files.
+
diff --git a/lexersrc/parser.lex b/lexersrc/parser.lex
new file mode 100644
index 0000000..1362f42
--- /dev/null
+++ b/lexersrc/parser.lex
@@ -0,0 +1,69 @@
+%{
+ /* Note for future hackers */
+ /* Jison by default gets the first matched item, working top down
+ * This is NOT the same as lex, which matches longest
+ * Adding the %option flex to the file will make jison work like lex
+ * but, this is not heavily tested
+ *
+ * The safest thing to do is have more important rules before less
+ * important rules, which is why . is last
+ */
+%}
+int "-"?([0-9]|[1-9][0-9]+)
+exp [eE][-+]?[0-9]+
+frac "."[0-9]+
+
+%x json singleQuoteString dblQuoteString
+%flex
+%%
+
+[^{,][^,]+ return 'LITERAL';
+"{" {
+ saver.currText = "{";
+ this.begin('json');
+}
+"," {
+ // do nothing we don't want commas
+}
+<> return 'EOF';
+
+"}" {
+ saver.currText = saver.currText + yytext;
+ if (saver.braceCount == 0) {
+ yytext = saver.currText;
+ saver.currText = "";
+ this.popState();
+ return 'JSON';
+ }
+ else {
+ saver.braceCount -= 1;
+ }
+}
+"{" {
+ saver.currText = saver.currText + yytext;
+ saver.braceCount += 1;
+}
+<> {
+ yytext = saver.currText;
+ saver.currText = "";
+ this.popState();
+ return 'EOF';
+}
+\s+ { saver.currText = saver.currText + yytext; }
+"'" { saver.currText = saver.currText + yytext; this.begin('singleQuoteString'); }
+\" { saver.currText = saver.currText + yytext; this.begin('dblQuoteString'); }
+"//" { this.begin('singleLineComment'); }
+"/*" { this.begin('multiLineComment'); }
+. { saver.currText = saver.currText + yytext; }
+
+"\'" { saver.currText = saver.currText + yytext; }
+"'" { saver.currText = saver.currText + yytext; this.popState(); }
+. { saver.currText = saver.currText + yytext; }
+
+"\\\"" { saver.currText = saver.currText + yytext; }
+\" { saver.currText = saver.currText + yytext; this.popState(); }
+. { saver.currText = saver.currText + yytext; }
+
+%%
+
+var saver = { currText: "", braceCount:0 }
diff --git a/lexersrc/parser.y b/lexersrc/parser.y
new file mode 100644
index 0000000..de06015
--- /dev/null
+++ b/lexersrc/parser.y
@@ -0,0 +1,53 @@
+%start parsedargs
+
+%{
+
+%}
+
+%%
+
+parsedargs
+ : EOF
+ {
+ $$ = [undefined];
+ return $$
+ }
+ | arguments EOF
+ {
+ $$ = $1;
+ return $$;
+ }
+ ;
+arguments
+ : stringarg
+ {
+ $$ = [$1];
+ }
+ | JSON
+ {
+ $$ = [$1];
+ }
+ | arguments stringarg
+ {
+ $1.push($2);
+ $$ = $1;
+ }
+ | arguments JSON
+ {
+ $1.push($2)
+ $$ = $1;
+ }
+ ;
+
+stringarg
+ : LITERAL
+ {
+ $$ = $1;
+ }
+ | LITERAL stringarg
+ {
+ $$ = $1 + $2;
+ }
+ ;
+
+%%
diff --git a/package.json b/package.json
index 67be4b5..0241dfd 100644
--- a/package.json
+++ b/package.json
@@ -6,10 +6,12 @@
"license": "MIT",
"homepage": "http://codef0rmer.github.io/angular-dragdrop",
"main": "src/angular-dragdrop.js",
- "dependencies": {},
+ "dependencies": {
+ },
"devDependencies": {
"grunt": "~0.4.1",
- "grunt-karma": "~0.4.4"
+ "grunt-karma": "~0.4.4",
+ "jison":"*"
},
"scripts": {},
"repository": {
diff --git a/src/Parser.js b/src/Parser.js
new file mode 100644
index 0000000..0e3452d
--- /dev/null
+++ b/src/Parser.js
@@ -0,0 +1,697 @@
+/* parser generated by jison 0.4.13 */
+/*
+ Returns a Parser object of the following structure:
+
+ Parser: {
+ yy: {}
+ }
+
+ Parser.prototype: {
+ yy: {},
+ trace: function(),
+ symbols_: {associative list: name ==> number},
+ terminals_: {associative list: number ==> name},
+ productions_: [...],
+ performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
+ table: [...],
+ defaultActions: {...},
+ parseError: function(str, hash),
+ parse: function(input),
+
+ lexer: {
+ EOF: 1,
+ parseError: function(str, hash),
+ setInput: function(input),
+ input: function(),
+ unput: function(str),
+ more: function(),
+ less: function(n),
+ pastInput: function(),
+ upcomingInput: function(),
+ showPosition: function(),
+ test_match: function(regex_match_array, rule_index),
+ next: function(),
+ lex: function(),
+ begin: function(condition),
+ popState: function(),
+ _currentRules: function(),
+ topState: function(),
+ pushState: function(condition),
+
+ options: {
+ ranges: boolean (optional: true ==> token location info will include a .range[] member)
+ flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
+ backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
+ },
+
+ performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
+ rules: [...],
+ conditions: {associative list: name ==> set},
+ }
+ }
+
+
+ token location info (@$, _$, etc.): {
+ first_line: n,
+ last_line: n,
+ first_column: n,
+ last_column: n,
+ range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based)
+ }
+
+
+ the parseError function receives a 'hash' object with these members for lexer and parser errors: {
+ text: (matched text)
+ token: (the produced terminal token, if any)
+ line: (yylineno)
+ }
+ while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
+ loc: (yylloc)
+ expected: (string describing the set of expected tokens)
+ recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
+ }
+*/
+var Parser = (function(){
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"parsedargs":3,"EOF":4,"arguments":5,"stringarg":6,"JSON":7,"LITERAL":8,"$accept":0,"$end":1},
+terminals_: {2:"error",4:"EOF",7:"JSON",8:"LITERAL"},
+productions_: [0,[3,1],[3,2],[5,1],[5,1],[5,2],[5,2],[6,1],[6,2]],
+performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
+/* this == yyval */
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 1:
+ this.$ = [undefined];
+ return this.$
+
+break;
+case 2:
+ this.$ = $$[$0-1];
+ return this.$;
+
+break;
+case 3:
+ this.$ = [$$[$0]];
+
+break;
+case 4:
+ this.$ = [$$[$0]];
+
+break;
+case 5:
+ $$[$0-1].push($$[$0]);
+ this.$ = $$[$0-1];
+
+break;
+case 6:
+ $$[$0-1].push($$[$0])
+ this.$ = $$[$0-1];
+
+break;
+case 7:
+ this.$ = $$[$0];
+
+break;
+case 8:
+ this.$ = $$[$0-1] + $$[$0];
+
+break;
+}
+},
+table: [{3:1,4:[1,2],5:3,6:4,7:[1,5],8:[1,6]},{1:[3]},{1:[2,1]},{4:[1,7],6:8,7:[1,9],8:[1,6]},{4:[2,3],7:[2,3],8:[2,3]},{4:[2,4],7:[2,4],8:[2,4]},{4:[2,7],6:10,7:[2,7],8:[1,6]},{1:[2,2]},{4:[2,5],7:[2,5],8:[2,5]},{4:[2,6],7:[2,6],8:[2,6]},{4:[2,8],7:[2,8],8:[2,8]}],
+defaultActions: {2:[2,1],7:[2,2]},
+parseError: function parseError(str, hash) {
+ if (hash.recoverable) {
+ this.trace(str);
+ } else {
+ throw new Error(str);
+ }
+},
+parse: function parse(input) {
+ var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+ var args = lstack.slice.call(arguments, 1);
+ this.lexer.setInput(input);
+ this.lexer.yy = this.yy;
+ this.yy.lexer = this.lexer;
+ this.yy.parser = this;
+ if (typeof this.lexer.yylloc == 'undefined') {
+ this.lexer.yylloc = {};
+ }
+ var yyloc = this.lexer.yylloc;
+ lstack.push(yyloc);
+ var ranges = this.lexer.options && this.lexer.options.ranges;
+ if (typeof this.yy.parseError === 'function') {
+ this.parseError = this.yy.parseError;
+ } else {
+ this.parseError = Object.getPrototypeOf(this).parseError;
+ }
+ function popStack(n) {
+ stack.length = stack.length - 2 * n;
+ vstack.length = vstack.length - n;
+ lstack.length = lstack.length - n;
+ }
+ function lex() {
+ var token;
+ token = self.lexer.lex() || EOF;
+ if (typeof token !== 'number') {
+ token = self.symbols_[token] || token;
+ }
+ return token;
+ }
+ var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+ while (true) {
+ state = stack[stack.length - 1];
+ if (this.defaultActions[state]) {
+ action = this.defaultActions[state];
+ } else {
+ if (symbol === null || typeof symbol == 'undefined') {
+ symbol = lex();
+ }
+ action = table[state] && table[state][symbol];
+ }
+ if (typeof action === 'undefined' || !action.length || !action[0]) {
+ var errStr = '';
+ expected = [];
+ for (p in table[state]) {
+ if (this.terminals_[p] && p > TERROR) {
+ expected.push('\'' + this.terminals_[p] + '\'');
+ }
+ }
+ if (this.lexer.showPosition) {
+ errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + this.lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
+ } else {
+ errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
+ }
+ this.parseError(errStr, {
+ text: this.lexer.match,
+ token: this.terminals_[symbol] || symbol,
+ line: this.lexer.yylineno,
+ loc: yyloc,
+ expected: expected
+ });
+ }
+ if (action[0] instanceof Array && action.length > 1) {
+ throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
+ }
+ switch (action[0]) {
+ case 1:
+ stack.push(symbol);
+ vstack.push(this.lexer.yytext);
+ lstack.push(this.lexer.yylloc);
+ stack.push(action[1]);
+ symbol = null;
+ if (!preErrorSymbol) {
+ yyleng = this.lexer.yyleng;
+ yytext = this.lexer.yytext;
+ yylineno = this.lexer.yylineno;
+ yyloc = this.lexer.yylloc;
+ if (recovering > 0) {
+ recovering--;
+ }
+ } else {
+ symbol = preErrorSymbol;
+ preErrorSymbol = null;
+ }
+ break;
+ case 2:
+ len = this.productions_[action[1]][1];
+ yyval.$ = vstack[vstack.length - len];
+ yyval._$ = {
+ first_line: lstack[lstack.length - (len || 1)].first_line,
+ last_line: lstack[lstack.length - 1].last_line,
+ first_column: lstack[lstack.length - (len || 1)].first_column,
+ last_column: lstack[lstack.length - 1].last_column
+ };
+ if (ranges) {
+ yyval._$.range = [
+ lstack[lstack.length - (len || 1)].range[0],
+ lstack[lstack.length - 1].range[1]
+ ];
+ }
+ r = this.performAction.apply(yyval, [
+ yytext,
+ yyleng,
+ yylineno,
+ this.yy,
+ action[1],
+ vstack,
+ lstack
+ ].concat(args));
+ if (typeof r !== 'undefined') {
+ return r;
+ }
+ if (len) {
+ stack = stack.slice(0, -1 * len * 2);
+ vstack = vstack.slice(0, -1 * len);
+ lstack = lstack.slice(0, -1 * len);
+ }
+ stack.push(this.productions_[action[1]][0]);
+ vstack.push(yyval.$);
+ lstack.push(yyval._$);
+ newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+ stack.push(newState);
+ break;
+ case 3:
+ return true;
+ }
+ }
+ return true;
+}};
+
+
+
+/* generated by jison-lex 0.2.1 */
+var lexer = (function(){
+var lexer = {
+
+EOF:1,
+
+parseError:function parseError(str, hash) {
+ if (this.yy.parser) {
+ this.yy.parser.parseError(str, hash);
+ } else {
+ throw new Error(str);
+ }
+ },
+
+// resets the lexer, sets new input
+setInput:function (input) {
+ this._input = input;
+ this._more = this._backtrack = this.done = false;
+ this.yylineno = this.yyleng = 0;
+ this.yytext = this.matched = this.match = '';
+ this.conditionStack = ['INITIAL'];
+ this.yylloc = {
+ first_line: 1,
+ first_column: 0,
+ last_line: 1,
+ last_column: 0
+ };
+ if (this.options.ranges) {
+ this.yylloc.range = [0,0];
+ }
+ this.offset = 0;
+ return this;
+ },
+
+// consumes and returns one char from the input
+input:function () {
+ var ch = this._input[0];
+ this.yytext += ch;
+ this.yyleng++;
+ this.offset++;
+ this.match += ch;
+ this.matched += ch;
+ var lines = ch.match(/(?:\r\n?|\n).*/g);
+ if (lines) {
+ this.yylineno++;
+ this.yylloc.last_line++;
+ } else {
+ this.yylloc.last_column++;
+ }
+ if (this.options.ranges) {
+ this.yylloc.range[1]++;
+ }
+
+ this._input = this._input.slice(1);
+ return ch;
+ },
+
+// unshifts one char (or a string) into the input
+unput:function (ch) {
+ var len = ch.length;
+ var lines = ch.split(/(?:\r\n?|\n)/g);
+
+ this._input = ch + this._input;
+ this.yytext = this.yytext.substr(0, this.yytext.length - len - 1);
+ //this.yyleng -= len;
+ this.offset -= len;
+ var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+ this.match = this.match.substr(0, this.match.length - 1);
+ this.matched = this.matched.substr(0, this.matched.length - 1);
+
+ if (lines.length - 1) {
+ this.yylineno -= lines.length - 1;
+ }
+ var r = this.yylloc.range;
+
+ this.yylloc = {
+ first_line: this.yylloc.first_line,
+ last_line: this.yylineno + 1,
+ first_column: this.yylloc.first_column,
+ last_column: lines ?
+ (lines.length === oldLines.length ? this.yylloc.first_column : 0)
+ + oldLines[oldLines.length - lines.length].length - lines[0].length :
+ this.yylloc.first_column - len
+ };
+
+ if (this.options.ranges) {
+ this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+ }
+ this.yyleng = this.yytext.length;
+ return this;
+ },
+
+// When called from action, caches matched text and appends it on next action
+more:function () {
+ this._more = true;
+ return this;
+ },
+
+// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
+reject:function () {
+ if (this.options.backtrack_lexer) {
+ this._backtrack = true;
+ } else {
+ return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
+ text: "",
+ token: null,
+ line: this.yylineno
+ });
+
+ }
+ return this;
+ },
+
+// retain first n characters of the match
+less:function (n) {
+ this.unput(this.match.slice(n));
+ },
+
+// displays already matched input, i.e. for error messages
+pastInput:function () {
+ var past = this.matched.substr(0, this.matched.length - this.match.length);
+ return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+ },
+
+// displays upcoming input, i.e. for error messages
+upcomingInput:function () {
+ var next = this.match;
+ if (next.length < 20) {
+ next += this._input.substr(0, 20-next.length);
+ }
+ return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
+ },
+
+// displays the character position where the lexing error occurred, i.e. for error messages
+showPosition:function () {
+ var pre = this.pastInput();
+ var c = new Array(pre.length + 1).join("-");
+ return pre + this.upcomingInput() + "\n" + c + "^";
+ },
+
+// test the lexed token: return FALSE when not a match, otherwise return token
+test_match:function (match, indexed_rule) {
+ var token,
+ lines,
+ backup;
+
+ if (this.options.backtrack_lexer) {
+ // save context
+ backup = {
+ yylineno: this.yylineno,
+ yylloc: {
+ first_line: this.yylloc.first_line,
+ last_line: this.last_line,
+ first_column: this.yylloc.first_column,
+ last_column: this.yylloc.last_column
+ },
+ yytext: this.yytext,
+ match: this.match,
+ matches: this.matches,
+ matched: this.matched,
+ yyleng: this.yyleng,
+ offset: this.offset,
+ _more: this._more,
+ _input: this._input,
+ yy: this.yy,
+ conditionStack: this.conditionStack.slice(0),
+ done: this.done
+ };
+ if (this.options.ranges) {
+ backup.yylloc.range = this.yylloc.range.slice(0);
+ }
+ }
+
+ lines = match[0].match(/(?:\r\n?|\n).*/g);
+ if (lines) {
+ this.yylineno += lines.length;
+ }
+ this.yylloc = {
+ first_line: this.yylloc.last_line,
+ last_line: this.yylineno + 1,
+ first_column: this.yylloc.last_column,
+ last_column: lines ?
+ lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
+ this.yylloc.last_column + match[0].length
+ };
+ this.yytext += match[0];
+ this.match += match[0];
+ this.matches = match;
+ this.yyleng = this.yytext.length;
+ if (this.options.ranges) {
+ this.yylloc.range = [this.offset, this.offset += this.yyleng];
+ }
+ this._more = false;
+ this._backtrack = false;
+ this._input = this._input.slice(match[0].length);
+ this.matched += match[0];
+ token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
+ if (this.done && this._input) {
+ this.done = false;
+ }
+ if (token) {
+ return token;
+ } else if (this._backtrack) {
+ // recover context
+ for (var k in backup) {
+ this[k] = backup[k];
+ }
+ return false; // rule action called reject() implying the next rule should be tested instead.
+ }
+ return false;
+ },
+
+// return next match in input
+next:function () {
+ if (this.done) {
+ return this.EOF;
+ }
+ if (!this._input) {
+ this.done = true;
+ }
+
+ var token,
+ match,
+ tempMatch,
+ index;
+ if (!this._more) {
+ this.yytext = '';
+ this.match = '';
+ }
+ var rules = this._currentRules();
+ for (var i = 0; i < rules.length; i++) {
+ tempMatch = this._input.match(this.rules[rules[i]]);
+ if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+ match = tempMatch;
+ index = i;
+ if (this.options.backtrack_lexer) {
+ token = this.test_match(tempMatch, rules[i]);
+ if (token !== false) {
+ return token;
+ } else if (this._backtrack) {
+ match = false;
+ continue; // rule action called reject() implying a rule MISmatch.
+ } else {
+ // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+ return false;
+ }
+ } else if (!this.options.flex) {
+ break;
+ }
+ }
+ }
+ if (match) {
+ token = this.test_match(match, rules[index]);
+ if (token !== false) {
+ return token;
+ }
+ // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+ return false;
+ }
+ if (this._input === "") {
+ return this.EOF;
+ } else {
+ return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
+ text: "",
+ token: null,
+ line: this.yylineno
+ });
+ }
+ },
+
+// return next match that has a token
+lex:function lex() {
+ var r = this.next();
+ if (r) {
+ return r;
+ } else {
+ return this.lex();
+ }
+ },
+
+// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
+begin:function begin(condition) {
+ this.conditionStack.push(condition);
+ },
+
+// pop the previously active lexer condition state off the condition stack
+popState:function popState() {
+ var n = this.conditionStack.length - 1;
+ if (n > 0) {
+ return this.conditionStack.pop();
+ } else {
+ return this.conditionStack[0];
+ }
+ },
+
+// produce the lexer rule set which is active for the currently active lexer condition state
+_currentRules:function _currentRules() {
+ if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
+ return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+ } else {
+ return this.conditions["INITIAL"].rules;
+ }
+ },
+
+// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
+topState:function topState(n) {
+ n = this.conditionStack.length - 1 - Math.abs(n || 0);
+ if (n >= 0) {
+ return this.conditionStack[n];
+ } else {
+ return "INITIAL";
+ }
+ },
+
+// alias for begin(condition)
+pushState:function pushState(condition) {
+ this.begin(condition);
+ },
+
+// return the number of states currently on the stack
+stateStackSize:function stateStackSize() {
+ return this.conditionStack.length;
+ },
+options: {},
+performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+
+ /* Note for future hackers */
+ /* Jison by default gets the first matched item, working top down
+ * This is NOT the same as lex, which matches longest
+ * Adding the %option flex to the file will make jison work like lex
+ * but, this is not heavily tested
+ *
+ * The safest thing to do is have more important rules before less
+ * important rules, which is why . is last
+ */
+
+var YYSTATE=YY_START;
+switch($avoiding_name_collisions) {
+case 0:return 8;
+break;
+case 1:
+ saver.currText = "{";
+ this.begin('json');
+
+break;
+case 2:
+ // do nothing we don't want commas
+
+break;
+case 3:return 4;
+break;
+case 4:
+ saver.currText = saver.currText + yy_.yytext;
+ if (saver.braceCount == 0) {
+ yy_.yytext = saver.currText;
+ saver.currText = "";
+ this.popState();
+ return 7;
+ }
+ else {
+ saver.braceCount -= 1;
+ }
+
+break;
+case 5:
+ saver.currText = saver.currText + yy_.yytext;
+ saver.braceCount += 1;
+
+break;
+case 6:
+ yy_.yytext = saver.currText;
+ saver.currText = "";
+ this.popState();
+ return 4;
+
+break;
+case 7: saver.currText = saver.currText + yy_.yytext;
+break;
+case 8: saver.currText = saver.currText + yy_.yytext; this.begin('singleQuoteString');
+break;
+case 9: saver.currText = saver.currText + yy_.yytext; this.begin('dblQuoteString');
+break;
+case 10: this.begin('singleLineComment');
+break;
+case 11: this.begin('multiLineComment');
+break;
+case 12: saver.currText = saver.currText + yy_.yytext;
+break;
+case 13: saver.currText = saver.currText + yy_.yytext;
+break;
+case 14: saver.currText = saver.currText + yy_.yytext; this.popState();
+break;
+case 15: saver.currText = saver.currText + yy_.yytext;
+break;
+case 16: saver.currText = saver.currText + yy_.yytext;
+break;
+case 17: saver.currText = saver.currText + yy_.yytext; this.popState();
+break;
+case 18: saver.currText = saver.currText + yy_.yytext;
+break;
+}
+},
+rules: [/^(?:[^{,][^,]+)/,/^(?:\{)/,/^(?:,)/,/^(?:$)/,/^(?:\})/,/^(?:\{)/,/^(?:$)/,/^(?:\s+)/,/^(?:')/,/^(?:")/,/^(?:\/\/)/,/^(?:\/\*)/,/^(?:.)/,/^(?:\\')/,/^(?:')/,/^(?:.)/,/^(?:\\")/,/^(?:")/,/^(?:.)/],
+conditions: {"json":{"rules":[4,5,6,7,8,9,10,11,12],"inclusive":false},"singleQuoteString":{"rules":[13,14,15],"inclusive":false},"dblQuoteString":{"rules":[16,17,18],"inclusive":false},"INITIAL":{"rules":[0,1,2,3],"inclusive":true}}
+};
+var saver = { currText: "", braceCount:0 };
+return lexer;
+})();
+parser.lexer = lexer;
+function Parser () {
+ this.yy = {};
+}
+Parser.prototype = parser;parser.Parser = Parser;
+return new Parser;
+})();
+
+
+if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
+exports.parser = Parser;
+exports.Parser = Parser.Parser;
+exports.parse = function () { return Parser.parse.apply(Parser, arguments); };
+exports.main = function commonjsMain(args) {
+ if (!args[1]) {
+ console.log('Usage: '+args[0]+' FILE');
+ process.exit(1);
+ }
+ var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8");
+ return exports.parser.parse(source);
+};
+if (typeof module !== 'undefined' && require.main === module) {
+ exports.main(process.argv.slice(1));
+}
+}
\ No newline at end of file
diff --git a/src/angular-dragdrop.js b/src/angular-dragdrop.js
index b4ade31..114a2cd 100644
--- a/src/angular-dragdrop.js
+++ b/src/angular-dragdrop.js
@@ -27,299 +27,342 @@
* (c) 2013 Amit Gharat a.k.a codef0rmer - amitgharat.wordpress.com
*/
-(function (window, angular, undefined) {
-'use strict';
-
-var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$timeout', '$parse', function($timeout, $parse) {
- this.callEventCallback = function (scope, callbackName, event, ui) {
- if (!callbackName) return;
-
- var objExtract = extract(callbackName),
+(function(window, angular, undefined) {
+ 'use strict';
+
+ var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$timeout', '$parse',
+ function($timeout, $parse) {
+ this.callEventCallback = function(scope, callbackName, event, ui) {
+ if (!callbackName) return;
+ var self = this;
+ var objExtract = extract(callbackName),
callback = objExtract.callback,
constructor = objExtract.constructor,
args = [event, ui].concat(objExtract.args);
-
- // call either $scoped method i.e. $scope.dropCallback or constructor's method i.e. this.dropCallback
- scope.$apply((scope[callback] || scope[constructor][callback]).apply(scope, args));
-
- function extract(callbackName) {
- var atStartBracket = callbackName.indexOf('(') !== -1 ? callbackName.indexOf('(') : callbackName.length,
+
+ // call either $scoped method i.e. $scope.dropCallback or constructor's method i.e. this.dropCallback
+ scope.$apply((scope[callback] || scope[constructor][callback]).apply(scope, args));
+
+ function extract(callbackName) {
+ var atStartBracket = callbackName.indexOf('(') !== -1 ? callbackName.indexOf('(') : callbackName.length,
atEndBracket = callbackName.lastIndexOf(')') !== -1 ? callbackName.lastIndexOf(')') : callbackName.length,
args = callbackName.substring(atStartBracket + 1, atEndBracket), // matching function arguments inside brackets
constructor = callbackName.match(/^[^.]+.\s*/)[0].slice(0, -1); // matching a string upto a dot to check ctrl as syntax
- constructor = scope[constructor] && typeof scope[constructor].constructor === 'function' ? constructor : null;
-
- return {
- callback: callbackName.substring(constructor && constructor.length + 1 || 0, atStartBracket),
- args: (args && args.split(',') || []).map(function(item) { return $parse(item)(scope); }),
- constructor: constructor
+ constructor = scope[constructor] && typeof scope[constructor].constructor === 'function' ? constructor : null;
+ self.joinedArguments = [];
+ var parsedArgs = Parser.parse(args);
+ return {
+ callback: callbackName.substring(constructor && constructor.length + 1 || 0, atStartBracket),
+ args: parsedArgs.map(function(item) {
+ return $parse(item)(scope);
+ }),
+ constructor: constructor
+ };
}
- }
- };
-
- this.invokeDrop = function ($draggable, $droppable, event, ui) {
- var dragModel = '',
- dropModel = '',
- dragSettings = {},
- dropSettings = {},
- jqyoui_pos = null,
- dragItem = {},
- dropItem = {},
- dragModelValue,
- dropModelValue,
- $droppableDraggable = null,
- droppableScope = $droppable.scope(),
- draggableScope = $draggable.scope();
-
- dragModel = $draggable.ngattr('ng-model');
- dropModel = $droppable.ngattr('ng-model');
- dragModelValue = draggableScope.$eval(dragModel);
- dropModelValue = droppableScope.$eval(dropModel);
-
- $droppableDraggable = $droppable.find('[jqyoui-draggable]:last,[data-jqyoui-draggable]:last');
- dropSettings = droppableScope.$eval($droppable.attr('jqyoui-droppable') || $droppable.attr('data-jqyoui-droppable')) || [];
- dragSettings = draggableScope.$eval($draggable.attr('jqyoui-draggable') || $draggable.attr('data-jqyoui-draggable')) || [];
-
- // Helps pick up the right item
- dragSettings.index = this.fixIndex(draggableScope, dragSettings, dragModelValue);
- dropSettings.index = this.fixIndex(droppableScope, dropSettings, dropModelValue);
-
- jqyoui_pos = angular.isArray(dragModelValue) ? dragSettings.index : null;
- dragItem = angular.copy(angular.isArray(dragModelValue) ? dragModelValue[jqyoui_pos] : dragModelValue);
-
- if (angular.isArray(dropModelValue) && dropSettings && dropSettings.index !== undefined) {
- dropItem = dropModelValue[dropSettings.index];
- } else if (!angular.isArray(dropModelValue)) {
- dropItem = dropModelValue;
- } else {
- dropItem = {};
- }
- dropItem = angular.copy(dropItem);
-
- if (dragSettings.animate === true) {
- this.move($draggable, $droppableDraggable.length > 0 ? $droppableDraggable : $droppable, null, 'fast', dropSettings, null);
- this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [], $draggable.parent('[jqyoui-droppable],[data-jqyoui-droppable]'), jqyoui.startXY, 'fast', dropSettings, angular.bind(this, function() {
- $timeout(angular.bind(this, function() {
- // Do not move this into move() to avoid flickering issue
- $draggable.css({'position': 'relative', 'left': '', 'top': ''});
- // Angular v1.2 uses ng-hide to hide an element not display property
- // so we've to manually remove display:none set in this.move()
- $droppableDraggable.css({'position': 'relative', 'left': '', 'top': '', 'display': ''});
+ };
+
+ this.invokeDrop = function($draggable, $droppable, event, ui) {
+ var dragModel = '',
+ dropModel = '',
+ dragSettings = {},
+ dropSettings = {},
+ jqyoui_pos = null,
+ dragItem = {},
+ dropItem = {},
+ dragModelValue,
+ dropModelValue,
+ $droppableDraggable = null,
+ droppableScope = $droppable.scope(),
+ draggableScope = $draggable.scope();
+
+ dragModel = $draggable.ngattr('ng-model');
+ dropModel = $droppable.ngattr('ng-model');
+ dragModelValue = draggableScope.$eval(dragModel);
+ dropModelValue = droppableScope.$eval(dropModel);
+
+ $droppableDraggable = $droppable.find('[jqyoui-draggable]:last,[data-jqyoui-draggable]:last');
+ dropSettings = droppableScope.$eval($droppable.attr('jqyoui-droppable') || $droppable.attr('data-jqyoui-droppable')) || [];
+ dragSettings = draggableScope.$eval($draggable.attr('jqyoui-draggable') || $draggable.attr('data-jqyoui-draggable')) || [];
+
+ // Helps pick up the right item
+ dragSettings.index = this.fixIndex(draggableScope, dragSettings, dragModelValue);
+ dropSettings.index = this.fixIndex(droppableScope, dropSettings, dropModelValue);
+
+ jqyoui_pos = angular.isArray(dragModelValue) ? dragSettings.index : null;
+ dragItem = angular.copy(angular.isArray(dragModelValue) ? dragModelValue[jqyoui_pos] : dragModelValue);
+
+ if (angular.isArray(dropModelValue) && dropSettings && dropSettings.index !== undefined) {
+ dropItem = dropModelValue[dropSettings.index];
+ } else if (!angular.isArray(dropModelValue)) {
+ dropItem = dropModelValue;
+ } else {
+ dropItem = {};
+ }
+ dropItem = angular.copy(dropItem);
+
+ if (dragSettings.animate === true) {
+ this.move($draggable, $droppableDraggable.length > 0 ? $droppableDraggable : $droppable, null, 'fast', dropSettings, null);
+ this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [], $draggable.parent('[jqyoui-droppable],[data-jqyoui-droppable]'), jqyoui.startXY, 'fast', dropSettings, angular.bind(this, function() {
+ $timeout(angular.bind(this, function() {
+ // Do not move this into move() to avoid flickering issue
+ $draggable.css({
+ 'position': 'relative',
+ 'left': '',
+ 'top': ''
+ });
+ // Angular v1.2 uses ng-hide to hide an element not display property
+ // so we've to manually remove display:none set in this.move()
+ $droppableDraggable.css({
+ 'position': 'relative',
+ 'left': '',
+ 'top': '',
+ 'display': ''
+ });
+ this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
+ this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
+ this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui);
+ }));
+ }));
+ } else {
+ $timeout(angular.bind(this, function() {
this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui);
}));
- }));
- } else {
- $timeout(angular.bind(this, function() {
- this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
- this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
- this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui);
- }));
- }
- };
-
- this.move = function($fromEl, $toEl, toPos, duration, dropSettings, callback) {
- if ($fromEl.length === 0) {
- if (callback) {
- window.setTimeout(function() {
- callback();
- }, 300);
}
- return false;
- }
-
- var zIndex = 9999,
- fromPos = $fromEl[dropSettings.containment || 'offset'](),
- wasVisible = $toEl && $toEl.is(':visible'),
- hadNgHideCls = $toEl.hasClass('ng-hide');
-
- if (toPos === null && $toEl.length > 0) {
- if (($toEl.attr('jqyoui-draggable') || $toEl.attr('data-jqyoui-draggable')) !== undefined && $toEl.ngattr('ng-model') !== undefined && $toEl.is(':visible') && dropSettings && dropSettings.multiple) {
- toPos = $toEl[dropSettings.containment || 'offset']();
- if (dropSettings.stack === false) {
- toPos.left+= $toEl.outerWidth(true);
- } else {
- toPos.top+= $toEl.outerHeight(true);
+ };
+
+ this.move = function($fromEl, $toEl, toPos, duration, dropSettings, callback) {
+ if ($fromEl.length === 0) {
+ if (callback) {
+ window.setTimeout(function() {
+ callback();
+ }, 300);
}
- } else {
- // Angular v1.2 uses ng-hide to hide an element
- // so we've to remove it in order to grab its position
- if (hadNgHideCls) $toEl.removeClass('ng-hide');
- toPos = $toEl.css({'visibility': 'hidden', 'display': 'block'})[dropSettings.containment || 'offset']();
- $toEl.css({'visibility': '','display': wasVisible ? 'block' : 'none'});
+ return false;
}
- }
-
- $fromEl.css({'position': 'absolute', 'z-index': zIndex})
- .css(fromPos)
- .animate(toPos, duration, function() {
- // Angular v1.2 uses ng-hide to hide an element
- // and as we remove it above, we've to put it back to
- // hide the element (while swapping) if it was hidden already
- // because we remove the display:none in this.invokeDrop()
- if (hadNgHideCls) $toEl.addClass('ng-hide');
- if (callback) callback();
- });
- };
- this.mutateDroppable = function(scope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos) {
- var dropModelValue = scope.$eval(dropModel);
-
- scope.dndDragItem = dragItem;
+ var zIndex = 9999,
+ fromPos = $fromEl[dropSettings.containment || 'offset'](),
+ wasVisible = $toEl && $toEl.is(':visible'),
+ hadNgHideCls = $toEl.hasClass('ng-hide');
+
+ if (toPos === null && $toEl.length > 0) {
+ if (($toEl.attr('jqyoui-draggable') || $toEl.attr('data-jqyoui-draggable')) !== undefined && $toEl.ngattr('ng-model') !== undefined && $toEl.is(':visible') && dropSettings && dropSettings.multiple) {
+ toPos = $toEl[dropSettings.containment || 'offset']();
+ if (dropSettings.stack === false) {
+ toPos.left += $toEl.outerWidth(true);
+ } else {
+ toPos.top += $toEl.outerHeight(true);
+ }
+ } else {
+ // Angular v1.2 uses ng-hide to hide an element
+ // so we've to remove it in order to grab its position
+ if (hadNgHideCls) $toEl.removeClass('ng-hide');
+ toPos = $toEl.css({
+ 'visibility': 'hidden',
+ 'display': 'block'
+ })[dropSettings.containment || 'offset']();
+ $toEl.css({
+ 'visibility': '',
+ 'display': wasVisible ? 'block' : 'none'
+ });
+ }
+ }
- if (angular.isArray(dropModelValue)) {
- if (dropSettings && dropSettings.index >= 0) {
- dropModelValue[dropSettings.index] = dragItem;
+ $fromEl.css({
+ 'position': 'absolute',
+ 'z-index': zIndex
+ })
+ .css(fromPos)
+ .animate(toPos, duration, function() {
+ // Angular v1.2 uses ng-hide to hide an element
+ // and as we remove it above, we've to put it back to
+ // hide the element (while swapping) if it was hidden already
+ // because we remove the display:none in this.invokeDrop()
+ if (hadNgHideCls) $toEl.addClass('ng-hide');
+ if (callback) callback();
+ });
+ };
+
+ this.mutateDroppable = function(scope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos) {
+ var dropModelValue = scope.$eval(dropModel);
+
+ scope.dndDragItem = dragItem;
+
+ if (angular.isArray(dropModelValue)) {
+ if (dropSettings && dropSettings.index >= 0) {
+ dropModelValue[dropSettings.index] = dragItem;
+ } else {
+ dropModelValue.push(dragItem);
+ }
+ if (dragSettings && dragSettings.placeholder === true) {
+ dropModelValue[dropModelValue.length - 1]['jqyoui_pos'] = jqyoui_pos;
+ }
} else {
- dropModelValue.push(dragItem);
- }
- if (dragSettings && dragSettings.placeholder === true) {
- dropModelValue[dropModelValue.length - 1]['jqyoui_pos'] = jqyoui_pos;
- }
- } else {
- $parse(dropModel + ' = dndDragItem')(scope);
- if (dragSettings && dragSettings.placeholder === true) {
- dropModelValue['jqyoui_pos'] = jqyoui_pos;
+ $parse(dropModel + ' = dndDragItem')(scope);
+ if (dragSettings && dragSettings.placeholder === true) {
+ dropModelValue['jqyoui_pos'] = jqyoui_pos;
+ }
}
- }
- };
+ };
- this.mutateDraggable = function(scope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable) {
- var isEmpty = angular.equals(dropItem, {}),
- dragModelValue = scope.$eval(dragModel);
+ this.mutateDraggable = function(scope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable) {
+ var isEmpty = angular.equals(dropItem, {}),
+ dragModelValue = scope.$eval(dragModel);
- scope.dndDropItem = dropItem;
+ scope.dndDropItem = dropItem;
- if (dragSettings && dragSettings.placeholder) {
- if (dragSettings.placeholder != 'keep'){
- if (angular.isArray(dragModelValue) && dragSettings.index !== undefined) {
- dragModelValue[dragSettings.index] = dropItem;
- } else {
- $parse(dragModel + ' = dndDropItem')(scope);
- }
- }
- } else {
- if (angular.isArray(dragModelValue)) {
- if (isEmpty) {
- if (dragSettings && ( dragSettings.placeholder !== true && dragSettings.placeholder !== 'keep' )) {
- dragModelValue.splice(dragSettings.index, 1);
+ if (dragSettings && dragSettings.placeholder) {
+ if (dragSettings.placeholder != 'keep') {
+ if (angular.isArray(dragModelValue) && dragSettings.index !== undefined) {
+ dragModelValue[dragSettings.index] = dropItem;
+ } else {
+ $parse(dragModel + ' = dndDropItem')(scope);
}
- } else {
- dragModelValue[dragSettings.index] = dropItem;
}
} else {
- // Fix: LIST(object) to LIST(array) - model does not get updated using just scope[dragModel] = {...}
- // P.S.: Could not figure out why it happened
- $parse(dragModel + ' = dndDropItem')(scope);
- if (scope.$parent) {
- $parse(dragModel + ' = dndDropItem')(scope.$parent);
+ if (angular.isArray(dragModelValue)) {
+ if (isEmpty) {
+ if (dragSettings && (dragSettings.placeholder !== true && dragSettings.placeholder !== 'keep')) {
+ dragModelValue.splice(dragSettings.index, 1);
+ }
+ } else {
+ dragModelValue[dragSettings.index] = dropItem;
+ }
+ } else {
+ // Fix: LIST(object) to LIST(array) - model does not get updated using just scope[dragModel] = {...}
+ // P.S.: Could not figure out why it happened
+ $parse(dragModel + ' = dndDropItem')(scope);
+ if (scope.$parent) {
+ $parse(dragModel + ' = dndDropItem')(scope.$parent);
+ }
}
}
- }
- $draggable.css({'z-index': '', 'left': '', 'top': ''});
- };
+ $draggable.css({
+ 'z-index': '',
+ 'left': '',
+ 'top': ''
+ });
+ };
- this.fixIndex = function(scope, settings, modelValue) {
- if (settings.applyFilter && angular.isArray(modelValue) && modelValue.length > 0) {
- var dragModelValueFiltered = scope[settings.applyFilter](),
+ this.fixIndex = function(scope, settings, modelValue) {
+ if (settings.applyFilter && angular.isArray(modelValue) && modelValue.length > 0) {
+ var dragModelValueFiltered = scope[settings.applyFilter](),
lookup = dragModelValueFiltered[settings.index],
- actualIndex = undefined;
+ actualIndex;
- modelValue.forEach(function(item, i) {
- if (angular.equals(item, lookup)) {
- actualIndex = i;
- }
- });
+ modelValue.forEach(function(item, i) {
+ if (angular.equals(item, lookup)) {
+ actualIndex = i;
+ }
+ });
- return actualIndex;
- }
-
- return settings.index;
- };
- }]).directive('jqyouiDraggable', ['ngDragDropService', function(ngDragDropService) {
- return {
- require: '?jqyouiDroppable',
- restrict: 'A',
- link: function(scope, element, attrs) {
- var dragSettings, jqyouiOptions, zIndex;
- var updateDraggable = function(newValue, oldValue) {
- if (newValue) {
- dragSettings = scope.$eval(element.attr('jqyoui-draggable') || element.attr('data-jqyoui-draggable')) || {};
- jqyouiOptions = scope.$eval(attrs.jqyouiOptions) || {};
- element
- .draggable({disabled: false})
- .draggable(jqyouiOptions)
- .draggable({
- start: function(event, ui) {
- zIndex = angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index');
- angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index', 9999);
- jqyoui.startXY = angular.element(this)[dragSettings.containment || 'offset']();
- ngDragDropService.callEventCallback(scope, dragSettings.onStart, event, ui);
- },
- stop: function(event, ui) {
- angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index', zIndex);
- ngDragDropService.callEventCallback(scope, dragSettings.onStop, event, ui);
- },
- drag: function(event, ui) {
- ngDragDropService.callEventCallback(scope, dragSettings.onDrag, event, ui);
- }
- });
- } else {
- element.draggable({disabled: true});
- }
- };
- scope.$watch(function() { return scope.$eval(attrs.drag); }, updateDraggable);
- updateDraggable();
+ return actualIndex;
+ }
- element.on('$destroy', function() {
- element.draggable('destroy');
- });
- }
- };
- }]).directive('jqyouiDroppable', ['ngDragDropService', function(ngDragDropService) {
- return {
- restrict: 'A',
- priority: 1,
- link: function(scope, element, attrs) {
- var dropSettings;
- var updateDroppable = function(newValue, oldValue) {
- if (newValue) {
- dropSettings = scope.$eval(angular.element(element).attr('jqyoui-droppable') || angular.element(element).attr('data-jqyoui-droppable')) || {};
- element
- .droppable({disabled: false})
- .droppable(scope.$eval(attrs.jqyouiOptions) || {})
- .droppable({
- over: function(event, ui) {
- ngDragDropService.callEventCallback(scope, dropSettings.onOver, event, ui);
- },
- out: function(event, ui) {
- ngDragDropService.callEventCallback(scope, dropSettings.onOut, event, ui);
- },
- drop: function(event, ui) {
- if (angular.element(ui.draggable).ngattr('ng-model') && attrs.ngModel) {
- ngDragDropService.invokeDrop(angular.element(ui.draggable), angular.element(this), event, ui);
- } else {
- ngDragDropService.callEventCallback(scope, dropSettings.onDrop, event, ui);
+ return settings.index;
+ };
+ }
+ ]).directive('jqyouiDraggable', ['ngDragDropService',
+ function(ngDragDropService) {
+ return {
+ require: '?jqyouiDroppable',
+ restrict: 'A',
+ link: function(scope, element, attrs) {
+ var dragSettings, jqyouiOptions, zIndex;
+ var updateDraggable = function(newValue, oldValue) {
+ if (newValue) {
+ dragSettings = scope.$eval(element.attr('jqyoui-draggable') || element.attr('data-jqyoui-draggable')) || {};
+ jqyouiOptions = scope.$eval(attrs.jqyouiOptions) || {};
+ element
+ .draggable({
+ disabled: false
+ })
+ .draggable(jqyouiOptions)
+ .draggable({
+ start: function(event, ui) {
+ zIndex = angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index');
+ angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index', 9999);
+ jqyoui.startXY = angular.element(this)[dragSettings.containment || 'offset']();
+ ngDragDropService.callEventCallback(scope, dragSettings.onStart, event, ui);
+ },
+ stop: function(event, ui) {
+ angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index', zIndex);
+ ngDragDropService.callEventCallback(scope, dragSettings.onStop, event, ui);
+ },
+ drag: function(event, ui) {
+ ngDragDropService.callEventCallback(scope, dragSettings.onDrag, event, ui);
}
- }
+ });
+ } else {
+ element.draggable({
+ disabled: true
});
- } else {
- element.droppable({disabled: true});
- }
- };
+ }
+ };
+ scope.$watch(function() {
+ return scope.$eval(attrs.drag);
+ }, updateDraggable);
+ updateDraggable();
+
+ element.on('$destroy', function() {
+ element.draggable('destroy');
+ });
+ }
+ };
+ }
+ ]).directive('jqyouiDroppable', ['ngDragDropService',
+ function(ngDragDropService) {
+ return {
+ restrict: 'A',
+ priority: 1,
+ link: function(scope, element, attrs) {
+ var dropSettings;
+ var updateDroppable = function(newValue, oldValue) {
+ if (newValue) {
+ dropSettings = scope.$eval(angular.element(element).attr('jqyoui-droppable') || angular.element(element).attr('data-jqyoui-droppable')) || {};
+ element
+ .droppable({
+ disabled: false
+ })
+ .droppable(scope.$eval(attrs.jqyouiOptions) || {})
+ .droppable({
+ over: function(event, ui) {
+ ngDragDropService.callEventCallback(scope, dropSettings.onOver, event, ui);
+ },
+ out: function(event, ui) {
+ ngDragDropService.callEventCallback(scope, dropSettings.onOut, event, ui);
+ },
+ drop: function(event, ui) {
+ if (angular.element(ui.draggable).ngattr('ng-model') && attrs.ngModel) {
+ ngDragDropService.invokeDrop(angular.element(ui.draggable), angular.element(this), event, ui);
+ } else {
+ ngDragDropService.callEventCallback(scope, dropSettings.onDrop, event, ui);
+ }
+ }
+ });
+ } else {
+ element.droppable({
+ disabled: true
+ });
+ }
+ };
- scope.$watch(function() { return scope.$eval(attrs.drop); }, updateDroppable);
- updateDroppable();
+ scope.$watch(function() {
+ return scope.$eval(attrs.drop);
+ }, updateDroppable);
+ updateDroppable();
- element.on('$destroy', function() {
- element.droppable('destroy');
- });
- }
- };
- }]);
+ element.on('$destroy', function() {
+ element.droppable('destroy');
+ });
+ }
+ };
+ }
+ ]);
$.fn.ngattr = function(name, value) {
var element = angular.element(this).get(0);
diff --git a/src/angular-dragdrop.min.js b/src/angular-dragdrop.min.js
index ec57732..4ff6611 100644
--- a/src/angular-dragdrop.min.js
+++ b/src/angular-dragdrop.min.js
@@ -16,14 +16,11 @@
* 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.
- */
-
-/**
+ *//**
* Implementing Drag and Drop functionality in AngularJS is easier than ever.
* Demo: http://codef0rmer.github.com/angular-dragdrop/
- *
+ *
* @version 1.0.7
*
* (c) 2013 Amit Gharat a.k.a codef0rmer - amitgharat.wordpress.com
- */
-(function(e,t,n){"use strict";var r=t.module("ngDragDrop",[]).service("ngDragDropService",["$timeout","$parse",function(i,s){this.callEventCallback=function(e,t,n,r){function f(t){var n=t.indexOf("(")!==-1?t.indexOf("("):t.length,r=t.lastIndexOf(")")!==-1?t.lastIndexOf(")"):t.length,i=t.substring(n+1,r),o=t.match(/^[^.]+.\s*/)[0].slice(0,-1);o=e[o]&&typeof e[o].constructor==="function"?o:null;return{callback:t.substring(o&&o.length+1||0,n),args:(i&&i.split(",")||[]).map(function(t){return s(t)(e)}),constructor:o}}if(!t)return;var i=f(t),o=i.callback,u=i.constructor,a=[n,r].concat(i.args);e.$apply((e[o]||e[u][o]).apply(e,a))};this.invokeDrop=function(e,s,o,u){var a="",f="",l={},c={},h=null,p={},d={},v,m,g=null,y=s.scope(),b=e.scope();a=e.ngattr("ng-model");f=s.ngattr("ng-model");v=b.$eval(a);m=y.$eval(f);g=s.find("[jqyoui-draggable]:last,[data-jqyoui-draggable]:last");c=y.$eval(s.attr("jqyoui-droppable")||s.attr("data-jqyoui-droppable"))||[];l=b.$eval(e.attr("jqyoui-draggable")||e.attr("data-jqyoui-draggable"))||[];l.index=this.fixIndex(b,l,v);c.index=this.fixIndex(y,c,m);h=t.isArray(v)?l.index:null;p=t.copy(t.isArray(v)?v[h]:v);if(t.isArray(m)&&c&&c.index!==n){d=m[c.index]}else if(!t.isArray(m)){d=m}else{d={}}d=t.copy(d);if(l.animate===true){this.move(e,g.length>0?g:s,null,"fast",c,null);this.move(g.length>0&&!c.multiple?g:[],e.parent("[jqyoui-droppable],[data-jqyoui-droppable]"),r.startXY,"fast",c,t.bind(this,function(){i(t.bind(this,function(){e.css({position:"relative",left:"",top:""});g.css({position:"relative",left:"",top:"",display:""});this.mutateDraggable(b,c,l,a,f,d,e);this.mutateDroppable(y,c,l,f,p,h);this.callEventCallback(y,c.onDrop,o,u)}))}))}else{i(t.bind(this,function(){this.mutateDraggable(b,c,l,a,f,d,e);this.mutateDroppable(y,c,l,f,p,h);this.callEventCallback(y,c.onDrop,o,u)}))}};this.move=function(t,r,i,s,o,u){if(t.length===0){if(u){e.setTimeout(function(){u()},300)}return false}var a=9999,f=t[o.containment||"offset"](),l=r&&r.is(":visible"),c=r.hasClass("ng-hide");if(i===null&&r.length>0){if((r.attr("jqyoui-draggable")||r.attr("data-jqyoui-draggable"))!==n&&r.ngattr("ng-model")!==n&&r.is(":visible")&&o&&o.multiple){i=r[o.containment||"offset"]();if(o.stack===false){i.left+=r.outerWidth(true)}else{i.top+=r.outerHeight(true)}}else{if(c)r.removeClass("ng-hide");i=r.css({visibility:"hidden",display:"block"})[o.containment||"offset"]();r.css({visibility:"",display:l?"block":"none"})}}t.css({position:"absolute","z-index":a}).css(f).animate(i,s,function(){if(c)r.addClass("ng-hide");if(u)u()})};this.mutateDroppable=function(e,n,r,i,o,u){var a=e.$eval(i);e.dndDragItem=o;if(t.isArray(a)){if(n&&n.index>=0){a[n.index]=o}else{a.push(o)}if(r&&r.placeholder===true){a[a.length-1]["jqyoui_pos"]=u}}else{s(i+" = dndDragItem")(e);if(r&&r.placeholder===true){a["jqyoui_pos"]=u}}};this.mutateDraggable=function(e,r,i,o,u,a,f){var l=t.equals(a,{}),c=e.$eval(o);e.dndDropItem=a;if(i&&i.placeholder){if(i.placeholder!="keep"){if(t.isArray(c)&&i.index!==n){c[i.index]=a}else{s(o+" = dndDropItem")(e)}}}else{if(t.isArray(c)){if(l){if(i&&i.placeholder!==true&&i.placeholder!=="keep"){c.splice(i.index,1)}}else{c[i.index]=a}}else{s(o+" = dndDropItem")(e);if(e.$parent){s(o+" = dndDropItem")(e.$parent)}}}f.css({"z-index":"",left:"",top:""})};this.fixIndex=function(e,r,i){if(r.applyFilter&&t.isArray(i)&&i.length>0){var s=e[r.applyFilter](),o=s[r.index],u=n;i.forEach(function(e,n){if(t.equals(e,o)){u=n}});return u}return r.index}}]).directive("jqyouiDraggable",["ngDragDropService",function(e){return{require:"?jqyouiDroppable",restrict:"A",link:function(n,i,s){var o,u,a;var f=function(f,l){if(f){o=n.$eval(i.attr("jqyoui-draggable")||i.attr("data-jqyoui-draggable"))||{};u=n.$eval(s.jqyouiOptions)||{};i.draggable({disabled:false}).draggable(u).draggable({start:function(i,s){a=t.element(u.helper?s.helper:this).css("z-index");t.element(u.helper?s.helper:this).css("z-index",9999);r.startXY=t.element(this)[o.containment||"offset"]();e.callEventCallback(n,o.onStart,i,s)},stop:function(r,i){t.element(u.helper?i.helper:this).css("z-index",a);e.callEventCallback(n,o.onStop,r,i)},drag:function(t,r){e.callEventCallback(n,o.onDrag,t,r)}})}else{i.draggable({disabled:true})}};n.$watch(function(){return n.$eval(s.drag)},f);f();i.on("$destroy",function(){i.draggable("destroy")})}}}]).directive("jqyouiDroppable",["ngDragDropService",function(e){return{restrict:"A",priority:1,link:function(n,r,i){var s;var o=function(o,u){if(o){s=n.$eval(t.element(r).attr("jqyoui-droppable")||t.element(r).attr("data-jqyoui-droppable"))||{};r.droppable({disabled:false}).droppable(n.$eval(i.jqyouiOptions)||{}).droppable({over:function(t,r){e.callEventCallback(n,s.onOver,t,r)},out:function(t,r){e.callEventCallback(n,s.onOut,t,r)},drop:function(r,o){if(t.element(o.draggable).ngattr("ng-model")&&i.ngModel){e.invokeDrop(t.element(o.draggable),t.element(this),r,o)}else{e.callEventCallback(n,s.onDrop,r,o)}}})}else{r.droppable({disabled:true})}};n.$watch(function(){return n.$eval(i.drop)},o);o();r.on("$destroy",function(){r.droppable("destroy")})}}}]);$.fn.ngattr=function(e,n){var r=t.element(this).get(0);return r.getAttribute(e)||r.getAttribute("data-"+e)}})(window,window.angular)
+ */(function(e,t,n){"use strict";var r=t.module("ngDragDrop",[]).service("ngDragDropService",["$timeout","$parse",function(i,s){this.callEventCallback=function(e,t,n,r){function l(t){var n=t.indexOf("(")!==-1?t.indexOf("("):t.length,r=t.lastIndexOf(")")!==-1?t.lastIndexOf(")"):t.length,o=t.substring(n+1,r),u=t.match(/^[^.]+.\s*/)[0].slice(0,-1);u=e[u]&&typeof e[u].constructor=="function"?u:null,i.joinedArguments=[];var a=Parser.parse(o);return{callback:t.substring(u&&u.length+1||0,n),args:a.map(function(t){return s(t)(e)}),constructor:u}}if(!t)return;var i=this,o=l(t),u=o.callback,a=o.constructor,f=[n,r].concat(o.args);e.$apply((e[u]||e[a][u]).apply(e,f))},this.invokeDrop=function(e,s,o,u){var a="",f="",l={},c={},h=null,p={},d={},v,m,g=null,y=s.scope(),b=e.scope();a=e.ngattr("ng-model"),f=s.ngattr("ng-model"),v=b.$eval(a),m=y.$eval(f),g=s.find("[jqyoui-draggable]:last,[data-jqyoui-draggable]:last"),c=y.$eval(s.attr("jqyoui-droppable")||s.attr("data-jqyoui-droppable"))||[],l=b.$eval(e.attr("jqyoui-draggable")||e.attr("data-jqyoui-draggable"))||[],l.index=this.fixIndex(b,l,v),c.index=this.fixIndex(y,c,m),h=t.isArray(v)?l.index:null,p=t.copy(t.isArray(v)?v[h]:v),t.isArray(m)&&c&&c.index!==n?d=m[c.index]:t.isArray(m)?d={}:d=m,d=t.copy(d),l.animate===!0?(this.move(e,g.length>0?g:s,null,"fast",c,null),this.move(g.length>0&&!c.multiple?g:[],e.parent("[jqyoui-droppable],[data-jqyoui-droppable]"),r.startXY,"fast",c,t.bind(this,function(){i(t.bind(this,function(){e.css({position:"relative",left:"",top:""}),g.css({position:"relative",left:"",top:"",display:""}),this.mutateDraggable(b,c,l,a,f,d,e),this.mutateDroppable(y,c,l,f,p,h),this.callEventCallback(y,c.onDrop,o,u)}))}))):i(t.bind(this,function(){this.mutateDraggable(b,c,l,a,f,d,e),this.mutateDroppable(y,c,l,f,p,h),this.callEventCallback(y,c.onDrop,o,u)}))},this.move=function(t,r,i,s,o,u){if(t.length===0)return u&&e.setTimeout(function(){u()},300),!1;var a=9999,f=t[o.containment||"offset"](),l=r&&r.is(":visible"),c=r.hasClass("ng-hide");i===null&&r.length>0&&((r.attr("jqyoui-draggable")||r.attr("data-jqyoui-draggable"))!==n&&r.ngattr("ng-model")!==n&&r.is(":visible")&&o&&o.multiple?(i=r[o.containment||"offset"](),o.stack===!1?i.left+=r.outerWidth(!0):i.top+=r.outerHeight(!0)):(c&&r.removeClass("ng-hide"),i=r.css({visibility:"hidden",display:"block"})[o.containment||"offset"](),r.css({visibility:"",display:l?"block":"none"}))),t.css({position:"absolute","z-index":a}).css(f).animate(i,s,function(){c&&r.addClass("ng-hide"),u&&u()})},this.mutateDroppable=function(e,n,r,i,o,u){var a=e.$eval(i);e.dndDragItem=o,t.isArray(a)?(n&&n.index>=0?a[n.index]=o:a.push(o),r&&r.placeholder===!0&&(a[a.length-1].jqyoui_pos=u)):(s(i+" = dndDragItem")(e),r&&r.placeholder===!0&&(a.jqyoui_pos=u))},this.mutateDraggable=function(e,r,i,o,u,a,f){var l=t.equals(a,{}),c=e.$eval(o);e.dndDropItem=a,i&&i.placeholder?i.placeholder!="keep"&&(t.isArray(c)&&i.index!==n?c[i.index]=a:s(o+" = dndDropItem")(e)):t.isArray(c)?l?i&&i.placeholder!==!0&&i.placeholder!=="keep"&&c.splice(i.index,1):c[i.index]=a:(s(o+" = dndDropItem")(e),e.$parent&&s(o+" = dndDropItem")(e.$parent)),f.css({"z-index":"",left:"",top:""})},this.fixIndex=function(e,n,r){if(n.applyFilter&&t.isArray(r)&&r.length>0){var i=e[n.applyFilter](),s=i[n.index],o;return r.forEach(function(e,n){t.equals(e,s)&&(o=n)}),o}return n.index}}]).directive("jqyouiDraggable",["ngDragDropService",function(e){return{require:"?jqyouiDroppable",restrict:"A",link:function(n,i,s){var o,u,a,f=function(f,l){f?(o=n.$eval(i.attr("jqyoui-draggable")||i.attr("data-jqyoui-draggable"))||{},u=n.$eval(s.jqyouiOptions)||{},i.draggable({disabled:!1}).draggable(u).draggable({start:function(i,s){a=t.element(u.helper?s.helper:this).css("z-index"),t.element(u.helper?s.helper:this).css("z-index",9999),r.startXY=t.element(this)[o.containment||"offset"](),e.callEventCallback(n,o.onStart,i,s)},stop:function(r,i){t.element(u.helper?i.helper:this).css("z-index",a),e.callEventCallback(n,o.onStop,r,i)},drag:function(t,r){e.callEventCallback(n,o.onDrag,t,r)}})):i.draggable({disabled:!0})};n.$watch(function(){return n.$eval(s.drag)},f),f(),i.on("$destroy",function(){i.draggable("destroy")})}}}]).directive("jqyouiDroppable",["ngDragDropService",function(e){return{restrict:"A",priority:1,link:function(n,r,i){var s,o=function(o,u){o?(s=n.$eval(t.element(r).attr("jqyoui-droppable")||t.element(r).attr("data-jqyoui-droppable"))||{},r.droppable({disabled:!1}).droppable(n.$eval(i.jqyouiOptions)||{}).droppable({over:function(t,r){e.callEventCallback(n,s.onOver,t,r)},out:function(t,r){e.callEventCallback(n,s.onOut,t,r)},drop:function(r,o){t.element(o.draggable).ngattr("ng-model")&&i.ngModel?e.invokeDrop(t.element(o.draggable),t.element(this),r,o):e.callEventCallback(n,s.onDrop,r,o)}})):r.droppable({disabled:!0})};n.$watch(function(){return n.$eval(i.drop)},o),o(),r.on("$destroy",function(){r.droppable("destroy")})}}}]);$.fn.ngattr=function(e,n){var r=t.element(this).get(0);return r.getAttribute(e)||r.getAttribute("data-"+e)}})(window,window.angular);
\ No newline at end of file
diff --git a/test/spec/tests.js b/test/spec/tests.js
index 82ea83f..2747e8b 100644
--- a/test/spec/tests.js
+++ b/test/spec/tests.js
@@ -169,4 +169,51 @@ describe('Service: ngDragDropService', function() {
scope.list = {title: 'Item 1'};
expect(ngDragDropService.fixIndex(scope, {applyFilter: 'filterIt'}, scope.list)).toBe(undefined);
});
+
+ it('should parse json arguments as json', function(){
+ var localScope = rootScope.$new();
+ var expectedData = {r1: 1, r2:2};
+ localScope.startDrag = function(event, ui, data){
+ expect(data).toEqual(expectedData);
+ };
+ var callbackName = "startDrag("+ JSON.stringify(expectedData) +")";
+ ngDragDropService.callEventCallback(localScope, callbackName, {}, {});
+ });
+
+ it('should parse json objects along with non json objects', function(){
+ var localScope = rootScope.$new();
+ var expectedData = {r1: 1, r2: 2};
+ var expectedFirstArg = "arg1";
+ localScope.arg1 = expectedFirstArg;
+ localScope.startDrag = function(event, ui,firstArg, data, thirdArg){
+ expect(firstArg).toEqual(expectedFirstArg);
+ expect(data).toEqual(expectedData);
+ };
+ var callbackName = "startDrag("+ expectedFirstArg +","+ JSON.stringify(expectedData) +")";
+ ngDragDropService.callEventCallback(localScope, callbackName, {}, {});
+ });
+
+ it('should parse nested json objects as a single argument', function(){
+ var localScope = rootScope.$new();
+ var expectedData = {r1: 1, r2: {rs1: "someId", rs2: "someother"}};
+ localScope.startDrag = function(event, ui, data){
+ expect(data).toEqual(expectedData);
+ };
+ var callbackName = "startDrag("+ JSON.stringify(expectedData) +")";
+ ngDragDropService.callEventCallback(localScope, callbackName, {}, {});
+ });
+
+ it('should parse empty arguments', function(){
+ var localScope = rootScope.$new();
+ var expectedEvent = {"foo": "bar"};
+ var expectedUI = {"bar":"baz"};
+ localScope.startDrag = function(event, ui, data){
+ expect(event).toEqual(expectedEvent);
+ expect(expectedUI).toEqual(expectedUI);
+ expect(data).toBe(undefined);
+ };
+ var callbackName = "startDrag()";
+ ngDragDropService.callEventCallback(localScope, callbackName, expectedEvent, expectedUI);
+ });
+
});
\ No newline at end of file
diff --git a/test/test.conf.js b/test/test.conf.js
index 015ed66..dd8df59 100644
--- a/test/test.conf.js
+++ b/test/test.conf.js
@@ -7,6 +7,7 @@ files = [
'components/angular/angular.js',
'components/angular-mocks/angular-mocks.js',
'src/angular-dragdrop.js',
+ 'src/Parser.js',
'test/spec/*.js'
];
singleRun = true;