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