0c017f82e09851c6ff90035866b55be9a71cd1cc
[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
49                    return doc.createFragment(doc.CaretFragment, {node: text, offset:0});
50                 }, {
51                     metadata: {
52                         description: action.getState().description
53                     },
54                     success: callback
55                 });
56             } else {
57                 throw new Error('Invalid boundries');
58             }
59         },
60         remove: function(callback, params) {
61             /* globals Node */
62             var current = params.fragment.node,
63                 action = this;
64
65             var toSearch = current.nodeType === Node.ELEMENT_NODE ? [current] : [];
66             toSearch = toSearch.concat(current.parents());
67             toSearch.some(function(node) {
68                 if(node.is('list')) {
69                     node.document.transaction(function() {
70                         node.object.extractListItems();
71                     }, {
72                         metadata: {
73                             description: action.getState().description
74                         },
75                         success: callback
76                     });
77                     
78                     return true; // break
79                 }
80             }.bind(this));
81         },
82         changeType: function(callback, params) {
83             var node = params.fragment.node,
84                 action = this;
85             node.document.transaction(function() {
86                 node.getParent('list').setClass(type === 'Bullet' ? 'list' : 'list.enum');
87             }, {
88                 metadata: {
89                     description: action.getState().description
90                 },
91                 success: callback
92             });
93         }
94     };
95
96     var isToggled = function(params) {
97         if(params.fragment && params.fragment.node && params.fragment.node.isInside('list')) {
98             var list = params.fragment.node.getParent('list');
99             return list.getClass() === (type === 'Bullet' ? 'list' : 'list.enum');
100         }
101         return false;
102     };
103
104     var label = type === 'Bullet' ? gettext('bull. list') : gettext('num. list');
105
106     return {
107         name: 'toggle' + type + 'List',
108         context: ['fragment'],
109         params: {
110             fragment: {type: 'context', name: 'fragment'}
111         },
112         stateDefaults: {
113             label: label
114         },
115         getState: function(params) {
116             if(!params.fragment || !params.fragment.isValid()) {
117                 return false;
118             }
119
120             if(params.fragment instanceof params.fragment.CaretFragment && params.fragment.node.isInside('list')) {
121                 var list = params.fragment.node.getParent('list');
122                 if((list.getClass() === 'list' && type === 'Enum') || (list.getClass() === 'list.enum' && type === 'Bullet')) {
123                     return {
124                         allowed: true,
125                         description: interpolate(gettext('Change list type to %s'), [label]),
126                         execute: execute.changeType
127                     };
128                 }
129                 return {
130                     allowed: true,
131                     toggled: isToggled(params),
132                     description: gettext('Remove list'),
133                     execute: execute.remove
134                 };
135
136             }
137             var boundries = getBoundriesForAList(params.fragment);
138             if(boundries) {
139                 return {
140                     allowed: true,
141                     description: interpolate(gettext('Make %s fragment(s) into list'), [countItems(getBoundriesForAList(params.fragment))]),
142                     execute: execute.add
143                 };
144             }
145         }
146     };
147 };
148
149
150 return {
151     actions: [toggleListAction('Bullet'), toggleListAction('Enum')]
152 };
153
154 });