sferik-merb-admin 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/LICENSE +20 -0
  2. data/README.markdown +55 -0
  3. data/Rakefile +69 -0
  4. data/app/controllers/application.rb +17 -0
  5. data/app/controllers/forms.rb +85 -0
  6. data/app/helpers/application_helper.rb +66 -0
  7. data/app/helpers/forms_helper.rb +112 -0
  8. data/app/views/forms/_big_decimal.html.erb +8 -0
  9. data/app/views/forms/_date.html.erb +8 -0
  10. data/app/views/forms/_date_time.html.erb +8 -0
  11. data/app/views/forms/_float.html.erb +8 -0
  12. data/app/views/forms/_integer.html.erb +12 -0
  13. data/app/views/forms/_string.html.erb +16 -0
  14. data/app/views/forms/_time.html.erb +8 -0
  15. data/app/views/forms/_true_class.html.erb +5 -0
  16. data/app/views/forms/delete.html.erb +12 -0
  17. data/app/views/forms/edit.html.erb +23 -0
  18. data/app/views/forms/index.html.erb +22 -0
  19. data/app/views/forms/list.html.erb +64 -0
  20. data/app/views/forms/new.html.erb +19 -0
  21. data/app/views/layout/_message.html.erb +10 -0
  22. data/app/views/layout/dashboard.html.erb +37 -0
  23. data/app/views/layout/form.html.erb +51 -0
  24. data/app/views/layout/list.html.erb +45 -0
  25. data/lib/merb-admin/merbtasks.rb +103 -0
  26. data/lib/merb-admin/slicetasks.rb +20 -0
  27. data/lib/merb-admin/spectasks.rb +53 -0
  28. data/lib/merb-admin.rb +101 -0
  29. data/public/images/arrow-down.gif +0 -0
  30. data/public/images/arrow-up.gif +0 -0
  31. data/public/images/changelist-bg.gif +0 -0
  32. data/public/images/changelist-bg_rtl.gif +0 -0
  33. data/public/images/chooser-bg.gif +0 -0
  34. data/public/images/chooser_stacked-bg.gif +0 -0
  35. data/public/images/default-bg-reverse.gif +0 -0
  36. data/public/images/default-bg.gif +0 -0
  37. data/public/images/deleted-overlay.gif +0 -0
  38. data/public/images/icon-no.gif +0 -0
  39. data/public/images/icon-unknown.gif +0 -0
  40. data/public/images/icon-yes.gif +0 -0
  41. data/public/images/icon_addlink.gif +0 -0
  42. data/public/images/icon_alert.gif +0 -0
  43. data/public/images/icon_calendar.gif +0 -0
  44. data/public/images/icon_changelink.gif +0 -0
  45. data/public/images/icon_clock.gif +0 -0
  46. data/public/images/icon_deletelink.gif +0 -0
  47. data/public/images/icon_error.gif +0 -0
  48. data/public/images/icon_searchbox.png +0 -0
  49. data/public/images/icon_success.gif +0 -0
  50. data/public/images/inline-delete-8bit.png +0 -0
  51. data/public/images/inline-delete.png +0 -0
  52. data/public/images/inline-restore-8bit.png +0 -0
  53. data/public/images/inline-restore.png +0 -0
  54. data/public/images/inline-splitter-bg.gif +0 -0
  55. data/public/images/nav-bg-grabber.gif +0 -0
  56. data/public/images/nav-bg-reverse.gif +0 -0
  57. data/public/images/nav-bg.gif +0 -0
  58. data/public/images/selector-add.gif +0 -0
  59. data/public/images/selector-addall.gif +0 -0
  60. data/public/images/selector-remove.gif +0 -0
  61. data/public/images/selector-removeall.gif +0 -0
  62. data/public/images/selector-search.gif +0 -0
  63. data/public/images/selector_stacked-add.gif +0 -0
  64. data/public/images/selector_stacked-remove.gif +0 -0
  65. data/public/images/tool-left.gif +0 -0
  66. data/public/images/tool-left_over.gif +0 -0
  67. data/public/images/tool-right.gif +0 -0
  68. data/public/images/tool-right_over.gif +0 -0
  69. data/public/images/tooltag-add.gif +0 -0
  70. data/public/images/tooltag-add_over.gif +0 -0
  71. data/public/images/tooltag-arrowright.gif +0 -0
  72. data/public/images/tooltag-arrowright_over.gif +0 -0
  73. data/public/javascripts/CollapsedFieldsets.js +85 -0
  74. data/public/javascripts/DateTimeShortcuts.js +255 -0
  75. data/public/javascripts/RelatedObjectLookups.js +96 -0
  76. data/public/javascripts/SelectBox.js +111 -0
  77. data/public/javascripts/SelectFilter2.js +113 -0
  78. data/public/javascripts/actions.js +39 -0
  79. data/public/javascripts/calendar.js +143 -0
  80. data/public/javascripts/core.js +176 -0
  81. data/public/javascripts/dateparse.js +233 -0
  82. data/public/javascripts/getElementsBySelector.js +167 -0
  83. data/public/javascripts/i18n.js +33 -0
  84. data/public/javascripts/master.js +0 -0
  85. data/public/javascripts/ordering.js +137 -0
  86. data/public/javascripts/timeparse.js +94 -0
  87. data/public/javascripts/urlify.js +140 -0
  88. data/public/stylesheets/base.css +746 -0
  89. data/public/stylesheets/changelists.css +269 -0
  90. data/public/stylesheets/dashboard.css +24 -0
  91. data/public/stylesheets/forms.css +327 -0
  92. data/public/stylesheets/global.css +142 -0
  93. data/public/stylesheets/ie.css +51 -0
  94. data/public/stylesheets/layout.css +29 -0
  95. data/public/stylesheets/login.css +54 -0
  96. data/public/stylesheets/master.css +2 -0
  97. data/public/stylesheets/null.css +1 -0
  98. data/public/stylesheets/patch-iewin.css +8 -0
  99. data/public/stylesheets/rtl.css +206 -0
  100. data/public/stylesheets/widgets.css +506 -0
  101. data/spec/controller/forms_spec.rb +41 -0
  102. data/spec/merb-admin_spec.rb +5 -0
  103. data/spec/spec_helper.rb +58 -0
  104. metadata +213 -0
@@ -0,0 +1,167 @@
1
+ /* document.getElementsBySelector(selector)
2
+ - returns an array of element objects from the current document
3
+ matching the CSS selector. Selectors can contain element names,
4
+ class names and ids and can be nested. For example:
5
+
6
+ elements = document.getElementsBySelect('div#main p a.external')
7
+
8
+ Will return an array of all 'a' elements with 'external' in their
9
+ class attribute that are contained inside 'p' elements that are
10
+ contained inside the 'div' element which has id="main"
11
+
12
+ New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
13
+ See http://www.w3.org/TR/css3-selectors/#attribute-selectors
14
+
15
+ Version 0.4 - Simon Willison, March 25th 2003
16
+ -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
17
+ -- Opera 7 fails
18
+ */
19
+
20
+ function getAllChildren(e) {
21
+ // Returns all children of element. Workaround required for IE5/Windows. Ugh.
22
+ return e.all ? e.all : e.getElementsByTagName('*');
23
+ }
24
+
25
+ document.getElementsBySelector = function(selector) {
26
+ // Attempt to fail gracefully in lesser browsers
27
+ if (!document.getElementsByTagName) {
28
+ return new Array();
29
+ }
30
+ // Split selector in to tokens
31
+ var tokens = selector.split(' ');
32
+ var currentContext = new Array(document);
33
+ for (var i = 0; i < tokens.length; i++) {
34
+ token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
35
+ if (token.indexOf('#') > -1) {
36
+ // Token is an ID selector
37
+ var bits = token.split('#');
38
+ var tagName = bits[0];
39
+ var id = bits[1];
40
+ var element = document.getElementById(id);
41
+ if (!element || (tagName && element.nodeName.toLowerCase() != tagName)) {
42
+ // ID not found or tag with that ID not found, return false.
43
+ return new Array();
44
+ }
45
+ // Set currentContext to contain just this element
46
+ currentContext = new Array(element);
47
+ continue; // Skip to next token
48
+ }
49
+ if (token.indexOf('.') > -1) {
50
+ // Token contains a class selector
51
+ var bits = token.split('.');
52
+ var tagName = bits[0];
53
+ var className = bits[1];
54
+ if (!tagName) {
55
+ tagName = '*';
56
+ }
57
+ // Get elements matching tag, filter them for class selector
58
+ var found = new Array;
59
+ var foundCount = 0;
60
+ for (var h = 0; h < currentContext.length; h++) {
61
+ var elements;
62
+ if (tagName == '*') {
63
+ elements = getAllChildren(currentContext[h]);
64
+ } else {
65
+ try {
66
+ elements = currentContext[h].getElementsByTagName(tagName);
67
+ }
68
+ catch(e) {
69
+ elements = [];
70
+ }
71
+ }
72
+ for (var j = 0; j < elements.length; j++) {
73
+ found[foundCount++] = elements[j];
74
+ }
75
+ }
76
+ currentContext = new Array;
77
+ var currentContextIndex = 0;
78
+ for (var k = 0; k < found.length; k++) {
79
+ if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
80
+ currentContext[currentContextIndex++] = found[k];
81
+ }
82
+ }
83
+ continue; // Skip to next token
84
+ }
85
+ // Code to deal with attribute selectors
86
+ if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
87
+ var tagName = RegExp.$1;
88
+ var attrName = RegExp.$2;
89
+ var attrOperator = RegExp.$3;
90
+ var attrValue = RegExp.$4;
91
+ if (!tagName) {
92
+ tagName = '*';
93
+ }
94
+ // Grab all of the tagName elements within current context
95
+ var found = new Array;
96
+ var foundCount = 0;
97
+ for (var h = 0; h < currentContext.length; h++) {
98
+ var elements;
99
+ if (tagName == '*') {
100
+ elements = getAllChildren(currentContext[h]);
101
+ } else {
102
+ elements = currentContext[h].getElementsByTagName(tagName);
103
+ }
104
+ for (var j = 0; j < elements.length; j++) {
105
+ found[foundCount++] = elements[j];
106
+ }
107
+ }
108
+ currentContext = new Array;
109
+ var currentContextIndex = 0;
110
+ var checkFunction; // This function will be used to filter the elements
111
+ switch (attrOperator) {
112
+ case '=': // Equality
113
+ checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
114
+ break;
115
+ case '~': // Match one of space seperated words
116
+ checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
117
+ break;
118
+ case '|': // Match start with value followed by optional hyphen
119
+ checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
120
+ break;
121
+ case '^': // Match starts with value
122
+ checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
123
+ break;
124
+ case '$': // Match ends with value - fails with "Warning" in Opera 7
125
+ checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
126
+ break;
127
+ case '*': // Match ends with value
128
+ checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
129
+ break;
130
+ default :
131
+ // Just test for existence of attribute
132
+ checkFunction = function(e) { return e.getAttribute(attrName); };
133
+ }
134
+ currentContext = new Array;
135
+ var currentContextIndex = 0;
136
+ for (var k = 0; k < found.length; k++) {
137
+ if (checkFunction(found[k])) {
138
+ currentContext[currentContextIndex++] = found[k];
139
+ }
140
+ }
141
+ // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
142
+ continue; // Skip to next token
143
+ }
144
+ // If we get here, token is JUST an element (not a class or ID selector)
145
+ tagName = token;
146
+ var found = new Array;
147
+ var foundCount = 0;
148
+ for (var h = 0; h < currentContext.length; h++) {
149
+ var elements = currentContext[h].getElementsByTagName(tagName);
150
+ for (var j = 0; j < elements.length; j++) {
151
+ found[foundCount++] = elements[j];
152
+ }
153
+ }
154
+ currentContext = found;
155
+ }
156
+ return currentContext;
157
+ }
158
+
159
+ /* That revolting regular expression explained
160
+ /^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
161
+ \---/ \---/\-------------/ \-------/
162
+ | | | |
163
+ | | | The value
164
+ | | ~,|,^,$,* or =
165
+ | Attribute
166
+ Tag
167
+ */
@@ -0,0 +1,33 @@
1
+ /* gettext library */
2
+
3
+ var catalog = new Array();
4
+
5
+ function pluralidx(count) { return (count == 1) ? 0 : 1; }
6
+
7
+ function gettext(msgid) {
8
+ var value = catalog[msgid];
9
+ if (typeof(value) == 'undefined') {
10
+ return msgid;
11
+ } else {
12
+ return (typeof(value) == 'string') ? value : value[0];
13
+ }
14
+ }
15
+
16
+ function ngettext(singular, plural, count) {
17
+ value = catalog[singular];
18
+ if (typeof(value) == 'undefined') {
19
+ return (count == 1) ? singular : plural;
20
+ } else {
21
+ return value[pluralidx(count)];
22
+ }
23
+ }
24
+
25
+ function gettext_noop(msgid) { return msgid; }
26
+
27
+ function interpolate(fmt, obj, named) {
28
+ if (named) {
29
+ return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
30
+ } else {
31
+ return fmt.replace(/%s/g, function(match){return String(obj.shift())});
32
+ }
33
+ }
File without changes
@@ -0,0 +1,137 @@
1
+ addEvent(window, 'load', reorder_init);
2
+
3
+ var lis;
4
+ var top = 0;
5
+ var left = 0;
6
+ var height = 30;
7
+
8
+ function reorder_init() {
9
+ lis = document.getElementsBySelector('ul#orderthese li');
10
+ var input = document.getElementsBySelector('input[name=order_]')[0];
11
+ setOrder(input.value.split(','));
12
+ input.disabled = true;
13
+ draw();
14
+ // Now initialise the dragging behaviour
15
+ var limit = (lis.length - 1) * height;
16
+ for (var i = 0; i < lis.length; i++) {
17
+ var li = lis[i];
18
+ var img = document.getElementById('handle'+li.id);
19
+ li.style.zIndex = 1;
20
+ Drag.init(img, li, left + 10, left + 10, top + 10, top + 10 + limit);
21
+ li.onDragStart = startDrag;
22
+ li.onDragEnd = endDrag;
23
+ img.style.cursor = 'move';
24
+ }
25
+ }
26
+
27
+ function submitOrderForm() {
28
+ var inputOrder = document.getElementsBySelector('input[name=order_]')[0];
29
+ inputOrder.value = getOrder();
30
+ inputOrder.disabled=false;
31
+ }
32
+
33
+ function startDrag() {
34
+ this.style.zIndex = '10';
35
+ this.className = 'dragging';
36
+ }
37
+
38
+ function endDrag(x, y) {
39
+ this.style.zIndex = '1';
40
+ this.className = '';
41
+ // Work out how far along it has been dropped, using x co-ordinate
42
+ var oldIndex = this.index;
43
+ var newIndex = Math.round((y - 10 - top) / height);
44
+ // 'Snap' to the correct position
45
+ this.style.top = (10 + top + newIndex * height) + 'px';
46
+ this.index = newIndex;
47
+ moveItem(oldIndex, newIndex);
48
+ }
49
+
50
+ function moveItem(oldIndex, newIndex) {
51
+ // Swaps two items, adjusts the index and left co-ord for all others
52
+ if (oldIndex == newIndex) {
53
+ return; // Nothing to swap;
54
+ }
55
+ var direction, lo, hi;
56
+ if (newIndex > oldIndex) {
57
+ lo = oldIndex;
58
+ hi = newIndex;
59
+ direction = -1;
60
+ } else {
61
+ direction = 1;
62
+ hi = oldIndex;
63
+ lo = newIndex;
64
+ }
65
+ var lis2 = new Array(); // We will build the new order in this array
66
+ for (var i = 0; i < lis.length; i++) {
67
+ if (i < lo || i > hi) {
68
+ // Position of items not between the indexes is unaffected
69
+ lis2[i] = lis[i];
70
+ continue;
71
+ } else if (i == newIndex) {
72
+ lis2[i] = lis[oldIndex];
73
+ continue;
74
+ } else {
75
+ // Item is between the two indexes - move it along 1
76
+ lis2[i] = lis[i - direction];
77
+ }
78
+ }
79
+ // Re-index everything
80
+ reIndex(lis2);
81
+ lis = lis2;
82
+ draw();
83
+ // document.getElementById('hiddenOrder').value = getOrder();
84
+ document.getElementsBySelector('input[name=order_]')[0].value = getOrder();
85
+ }
86
+
87
+ function reIndex(lis) {
88
+ for (var i = 0; i < lis.length; i++) {
89
+ lis[i].index = i;
90
+ }
91
+ }
92
+
93
+ function draw() {
94
+ for (var i = 0; i < lis.length; i++) {
95
+ var li = lis[i];
96
+ li.index = i;
97
+ li.style.position = 'absolute';
98
+ li.style.left = (10 + left) + 'px';
99
+ li.style.top = (10 + top + (i * height)) + 'px';
100
+ }
101
+ }
102
+
103
+ function getOrder() {
104
+ var order = new Array(lis.length);
105
+ for (var i = 0; i < lis.length; i++) {
106
+ order[i] = lis[i].id.substring(1, 100);
107
+ }
108
+ return order.join(',');
109
+ }
110
+
111
+ function setOrder(id_list) {
112
+ /* Set the current order to match the lsit of IDs */
113
+ var temp_lis = new Array();
114
+ for (var i = 0; i < id_list.length; i++) {
115
+ var id = 'p' + id_list[i];
116
+ temp_lis[temp_lis.length] = document.getElementById(id);
117
+ }
118
+ reIndex(temp_lis);
119
+ lis = temp_lis;
120
+ draw();
121
+ }
122
+
123
+ function addEvent(elm, evType, fn, useCapture)
124
+ // addEvent and removeEvent
125
+ // cross-browser event handling for IE5+, NS6 and Mozilla
126
+ // By Scott Andrew
127
+ {
128
+ if (elm.addEventListener){
129
+ elm.addEventListener(evType, fn, useCapture);
130
+ return true;
131
+ } else if (elm.attachEvent){
132
+ var r = elm.attachEvent("on"+evType, fn);
133
+ return r;
134
+ } else {
135
+ elm['on'+evType] = fn;
136
+ }
137
+ }
@@ -0,0 +1,94 @@
1
+ var timeParsePatterns = [
2
+ // 9
3
+ { re: /^\d{1,2}$/i,
4
+ handler: function(bits) {
5
+ if (bits[0].length == 1) {
6
+ return '0' + bits[0] + ':00';
7
+ } else {
8
+ return bits[0] + ':00';
9
+ }
10
+ }
11
+ },
12
+ // 13:00
13
+ { re: /^\d{2}[:.]\d{2}$/i,
14
+ handler: function(bits) {
15
+ return bits[0].replace('.', ':');
16
+ }
17
+ },
18
+ // 9:00
19
+ { re: /^\d[:.]\d{2}$/i,
20
+ handler: function(bits) {
21
+ return '0' + bits[0].replace('.', ':');
22
+ }
23
+ },
24
+ // 3 am / 3 a.m. / 3am
25
+ { re: /^(\d+)\s*([ap])(?:.?m.?)?$/i,
26
+ handler: function(bits) {
27
+ var hour = parseInt(bits[1]);
28
+ if (hour == 12) {
29
+ hour = 0;
30
+ }
31
+ if (bits[2].toLowerCase() == 'p') {
32
+ if (hour == 12) {
33
+ hour = 0;
34
+ }
35
+ return (hour + 12) + ':00';
36
+ } else {
37
+ if (hour < 10) {
38
+ return '0' + hour + ':00';
39
+ } else {
40
+ return hour + ':00';
41
+ }
42
+ }
43
+ }
44
+ },
45
+ // 3.30 am / 3:15 a.m. / 3.00am
46
+ { re: /^(\d+)[.:](\d{2})\s*([ap]).?m.?$/i,
47
+ handler: function(bits) {
48
+ var hour = parseInt(bits[1]);
49
+ var mins = parseInt(bits[2]);
50
+ if (mins < 10) {
51
+ mins = '0' + mins;
52
+ }
53
+ if (hour == 12) {
54
+ hour = 0;
55
+ }
56
+ if (bits[3].toLowerCase() == 'p') {
57
+ if (hour == 12) {
58
+ hour = 0;
59
+ }
60
+ return (hour + 12) + ':' + mins;
61
+ } else {
62
+ if (hour < 10) {
63
+ return '0' + hour + ':' + mins;
64
+ } else {
65
+ return hour + ':' + mins;
66
+ }
67
+ }
68
+ }
69
+ },
70
+ // noon
71
+ { re: /^no/i,
72
+ handler: function(bits) {
73
+ return '12:00';
74
+ }
75
+ },
76
+ // midnight
77
+ { re: /^mid/i,
78
+ handler: function(bits) {
79
+ return '00:00';
80
+ }
81
+ }
82
+ ];
83
+
84
+ function parseTimeString(s) {
85
+ for (var i = 0; i < timeParsePatterns.length; i++) {
86
+ var re = timeParsePatterns[i].re;
87
+ var handler = timeParsePatterns[i].handler;
88
+ var bits = re.exec(s);
89
+ if (bits) {
90
+ return handler(bits);
91
+ }
92
+ }
93
+ return s;
94
+ }
@@ -0,0 +1,140 @@
1
+ var LATIN_MAP = {
2
+ 'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç':
3
+ 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I',
4
+ 'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö':
5
+ 'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U',
6
+ 'Ý': 'Y', 'Þ': 'TH', 'ß': 'ss', 'à':'a', 'á':'a', 'â': 'a', 'ã': 'a', 'ä':
7
+ 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e',
8
+ 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó':
9
+ 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u',
10
+ 'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y'
11
+ }
12
+ var LATIN_SYMBOLS_MAP = {
13
+ '©':'(c)'
14
+ }
15
+ var GREEK_MAP = {
16
+ 'α':'a', 'β':'b', 'γ':'g', 'δ':'d', 'ε':'e', 'ζ':'z', 'η':'h', 'θ':'8',
17
+ 'ι':'i', 'κ':'k', 'λ':'l', 'μ':'m', 'ν':'n', 'ξ':'3', 'ο':'o', 'π':'p',
18
+ 'ρ':'r', 'σ':'s', 'τ':'t', 'υ':'y', 'φ':'f', 'χ':'x', 'ψ':'ps', 'ω':'w',
19
+ 'ά':'a', 'έ':'e', 'ί':'i', 'ό':'o', 'ύ':'y', 'ή':'h', 'ώ':'w', 'ς':'s',
20
+ 'ϊ':'i', 'ΰ':'y', 'ϋ':'y', 'ΐ':'i',
21
+ 'Α':'A', 'Β':'B', 'Γ':'G', 'Δ':'D', 'Ε':'E', 'Ζ':'Z', 'Η':'H', 'Θ':'8',
22
+ 'Ι':'I', 'Κ':'K', 'Λ':'L', 'Μ':'M', 'Ν':'N', 'Ξ':'3', 'Ο':'O', 'Π':'P',
23
+ 'Ρ':'R', 'Σ':'S', 'Τ':'T', 'Υ':'Y', 'Φ':'F', 'Χ':'X', 'Ψ':'PS', 'Ω':'W',
24
+ 'Ά':'A', 'Έ':'E', 'Ί':'I', 'Ό':'O', 'Ύ':'Y', 'Ή':'H', 'Ώ':'W', 'Ϊ':'I',
25
+ 'Ϋ':'Y'
26
+ }
27
+ var TURKISH_MAP = {
28
+ 'ş':'s', 'Ş':'S', 'ı':'i', 'İ':'I', 'ç':'c', 'Ç':'C', 'ü':'u', 'Ü':'U',
29
+ 'ö':'o', 'Ö':'O', 'ğ':'g', 'Ğ':'G'
30
+ }
31
+ var RUSSIAN_MAP = {
32
+ 'а':'a', 'б':'b', 'в':'v', 'г':'g', 'д':'d', 'е':'e', 'ё':'yo', 'ж':'zh',
33
+ 'з':'z', 'и':'i', 'й':'j', 'к':'k', 'л':'l', 'м':'m', 'н':'n', 'о':'o',
34
+ 'п':'p', 'р':'r', 'с':'s', 'т':'t', 'у':'u', 'ф':'f', 'х':'h', 'ц':'c',
35
+ 'ч':'ch', 'ш':'sh', 'щ':'sh', 'ъ':'', 'ы':'y', 'ь':'', 'э':'e', 'ю':'yu',
36
+ 'я':'ya',
37
+ 'А':'A', 'Б':'B', 'В':'V', 'Г':'G', 'Д':'D', 'Е':'E', 'Ё':'Yo', 'Ж':'Zh',
38
+ 'З':'Z', 'И':'I', 'Й':'J', 'К':'K', 'Л':'L', 'М':'M', 'Н':'N', 'О':'O',
39
+ 'П':'P', 'Р':'R', 'С':'S', 'Т':'T', 'У':'U', 'Ф':'F', 'Х':'H', 'Ц':'C',
40
+ 'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu',
41
+ 'Я':'Ya'
42
+ }
43
+ var UKRAINIAN_MAP = {
44
+ 'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g'
45
+ }
46
+ var CZECH_MAP = {
47
+ 'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u',
48
+ 'ž':'z', 'Č':'C', 'Ď':'D', 'Ě':'E', 'Ň': 'N', 'Ř':'R', 'Š':'S', 'Ť':'T',
49
+ 'Ů':'U', 'Ž':'Z'
50
+ }
51
+
52
+ var POLISH_MAP = {
53
+ 'ą':'a', 'ć':'c', 'ę':'e', 'ł':'l', 'ń':'n', 'ó':'o', 'ś':'s', 'ź':'z',
54
+ 'ż':'z', 'Ą':'A', 'Ć':'C', 'Ę':'e', 'Ł':'L', 'Ń':'N', 'Ó':'o', 'Ś':'S',
55
+ 'Ź':'Z', 'Ż':'Z'
56
+ }
57
+
58
+ var LATVIAN_MAP = {
59
+ 'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n',
60
+ 'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'i',
61
+ 'Ķ':'k', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'u', 'Ž':'Z'
62
+ }
63
+
64
+ var ALL_DOWNCODE_MAPS=new Array()
65
+ ALL_DOWNCODE_MAPS[0]=LATIN_MAP
66
+ ALL_DOWNCODE_MAPS[1]=LATIN_SYMBOLS_MAP
67
+ ALL_DOWNCODE_MAPS[2]=GREEK_MAP
68
+ ALL_DOWNCODE_MAPS[3]=TURKISH_MAP
69
+ ALL_DOWNCODE_MAPS[4]=RUSSIAN_MAP
70
+ ALL_DOWNCODE_MAPS[5]=UKRAINIAN_MAP
71
+ ALL_DOWNCODE_MAPS[6]=CZECH_MAP
72
+ ALL_DOWNCODE_MAPS[7]=POLISH_MAP
73
+ ALL_DOWNCODE_MAPS[8]=LATVIAN_MAP
74
+
75
+ var Downcoder = new Object();
76
+ Downcoder.Initialize = function()
77
+ {
78
+ if (Downcoder.map) // already made
79
+ return ;
80
+ Downcoder.map ={}
81
+ Downcoder.chars = '' ;
82
+ for(var i in ALL_DOWNCODE_MAPS)
83
+ {
84
+ var lookup = ALL_DOWNCODE_MAPS[i]
85
+ for (var c in lookup)
86
+ {
87
+ Downcoder.map[c] = lookup[c] ;
88
+ Downcoder.chars += c ;
89
+ }
90
+ }
91
+ Downcoder.regex = new RegExp('[' + Downcoder.chars + ']|[^' + Downcoder.chars + ']+','g') ;
92
+ }
93
+
94
+ downcode= function( slug )
95
+ {
96
+ Downcoder.Initialize() ;
97
+ var downcoded =""
98
+ var pieces = slug.match(Downcoder.regex);
99
+ if(pieces)
100
+ {
101
+ for (var i = 0 ; i < pieces.length ; i++)
102
+ {
103
+ if (pieces[i].length == 1)
104
+ {
105
+ var mapped = Downcoder.map[pieces[i]] ;
106
+ if (mapped != null)
107
+ {
108
+ downcoded+=mapped;
109
+ continue ;
110
+ }
111
+ }
112
+ downcoded+=pieces[i];
113
+ }
114
+ }
115
+ else
116
+ {
117
+ downcoded = slug;
118
+ }
119
+ return downcoded;
120
+ }
121
+
122
+
123
+ function URLify(s, num_chars) {
124
+ // changes, e.g., "Petty theft" to "petty_theft"
125
+ // remove all these words from the string before urlifying
126
+ s = downcode(s);
127
+ removelist = ["a", "an", "as", "at", "before", "but", "by", "for", "from",
128
+ "is", "in", "into", "like", "of", "off", "on", "onto", "per",
129
+ "since", "than", "the", "this", "that", "to", "up", "via",
130
+ "with"];
131
+ r = new RegExp('\\b(' + removelist.join('|') + ')\\b', 'gi');
132
+ s = s.replace(r, '');
133
+ // if downcode doesn't hit, the char will be stripped here
134
+ s = s.replace(/[^-\w\s]/g, ''); // remove unneeded chars
135
+ s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces
136
+ s = s.replace(/[-\s]+/g, '-'); // convert spaces to hyphens
137
+ s = s.toLowerCase(); // convert to lowercase
138
+ return s.substring(0, num_chars);// trim to first num_chars chars
139
+ }
140
+