7a9d3216c1144ac14c6affa873c3db5b81d2d98a
[fnpeditor.git] / src / editor / plugins / core / lists.js
1 define(function() {
2     
3 'use strict';
4 /* globals gettext, interpolate */
5
6
7 var getBoundriesForAList = function(fragment) {
8     var node;
9
10     if(fragment instanceof fragment.RangeFragment && fragment.hasSiblingBoundries()) {
11         return fragment.boundriesSiblingParents();
12     }
13     if(fragment instanceof fragment.NodeFragment) {
14         node = fragment.node.getNearestElementNode();
15         return {
16             node1: node,
17             node2: node
18         };
19     }
20 };
21
22 var countItems = function(boundries) {
23     var ptr = boundries.node1,
24         c = 1;
25     while(ptr && !ptr.sameNode(boundries.node2)) {
26         c++;
27         ptr = ptr.next();
28     }
29     return c;
30 };
31
32 var toggleListAction = function(type) {
33     
34     var execute = {
35         add: function(callback, params) {
36             var boundries = getBoundriesForAList(params.fragment),
37                 listParams = {klass: type === 'Bullet' ? 'list' : 'list.enum'},
38                 action = this;
39
40             if(boundries && boundries.node1) {
41                 listParams.node1 = boundries.node1;
42                 listParams.node2 = boundries.node2;
43                 boundries.node1.document.transaction(function() {
44                     var list = boundries.node1.document.createList(listParams),
45                         item1 = list.object.getItem(0),
46                         text = item1 ? item1.contents()[0] : undefined, //
47                         doc = boundries.node1.document;
48                     if(text) {
49                         return doc.createFragment(doc.CaretFragment, {node: text, offset:0});
50                     }
51                 }, {
52                     metadata: {
53                         description: action.getState().description,
54                         fragment: params.fragment
55                     },
56                     success: callback
57                 });
58             } else {
59                 throw new Error('Invalid boundries');
60             }
61         },
62         remove: function(callback, params) {
63             /* globals Node */
64             var current = params.fragment.node,
65                 action = this;
66
67             var toSearch = current.nodeType === Node.ELEMENT_NODE ? [current] : [];
68             toSearch = toSearch.concat(current.parents());
69             toSearch.some(function(node) {
70                 if(node.is('list')) {
71                     node.document.transaction(function() {
72                         var firstItem = node.object.extractListItems(),
73                             toret;
74                         if(params.fragment.isValid()) {
75                             toret = params.fragment;
76                         } else {
77                             toret = node.document.createFragment(node.document.NodeFragment, {node: firstItem});
78                         }
79                         return toret;
80                     }, {
81                         metadata: {
82                             description: action.getState().description,
83                             fragment: params.fragment
84                         },
85                         success: callback
86                     });
87                     
88                     return true; // break
89                 }
90             }.bind(this));
91         },
92         changeType: function(callback, params) {
93             var node = params.fragment.node,
94                 action = this;
95             node.document.transaction(function() {
96                 var list = node.getParent('list');
97                 list.setClass(type === 'Bullet' ? 'list' : 'list.enum');
98                 if(params.fragment.isValid()) {
99                     return params.fragment;
100                 } else {
101                     return node.document.createFragment(node.document.NodeFragment, {node: list.contents()[0]});
102                 }
103             }, {
104                 metadata: {
105                     description: action.getState().description,
106                     fragment: params.fragment
107                 },
108                 success: callback
109             });
110         }
111     };
112
113     var isToggled = function(params) {
114         if(params.fragment && params.fragment.node && params.fragment.node.isInside('list')) {
115             var list = params.fragment.node.getParent('list');
116             return list.getClass() === (type === 'Bullet' ? 'list' : 'list.enum');
117         }
118         return false;
119     };
120
121     var label = type === 'Bullet' ? gettext('bull. list') : gettext('num. list');
122
123     return {
124         name: 'toggle' + type + 'List',
125         context: ['fragment'],
126         params: {
127             fragment: {type: 'context', name: 'fragment'}
128         },
129         stateDefaults: {
130             label: label
131         },
132         getState: function(params) {
133             if(!params.fragment || !params.fragment.isValid()) {
134                 return false;
135             }
136
137             if(params.fragment instanceof params.fragment.CaretFragment && params.fragment.node.isInside('list')) {
138                 var list = params.fragment.node.getParent('list');
139                 if((list.getClass() === 'list' && type === 'Enum') || (list.getClass() === 'list.enum' && type === 'Bullet')) {
140                     return {
141                         allowed: true,
142                         description: interpolate(gettext('Change list type to %s'), [label]),
143                         execute: execute.changeType
144                     };
145                 }
146                 return {
147                     allowed: true,
148                     toggled: isToggled(params),
149                     description: gettext('Remove list'),
150                     execute: execute.remove
151                 };
152
153             }
154             var boundries = getBoundriesForAList(params.fragment);
155             if(boundries) {
156                 return {
157                     allowed: true,
158                     description: interpolate(gettext('Make %s fragment(s) into list'), [countItems(getBoundriesForAList(params.fragment))]),
159                     execute: execute.add
160                 };
161             }
162         }
163     };
164 };
165
166
167 return {
168     actions: [toggleListAction('Bullet'), toggleListAction('Enum')]
169 };
170
171 });