diff options
| author | erdgeist <erdgeist@erdgeist.org> | 2026-07-13 23:33:11 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@erdgeist.org> | 2026-07-13 23:33:11 +0200 |
| commit | 8eab68f2c5a3c126e2fec2ecdfa7ace87ce0937b (patch) | |
| tree | f446ebc26a7707c7b64a937aa51a155df146c80a /public | |
| parent | 42714c697273a7117c6b355fab26c8c35e336ad1 (diff) | |
| parent | cdf5d9941ca866d437612d2f863eac6eb0b3db12 (diff) | |
Merge branch 'erdgeist-revive-events'
Diffstat (limited to 'public')
| -rw-r--r-- | public/javascripts/admin_interface.js | 253 | ||||
| -rw-r--r-- | public/javascripts/admin_search.js | 393 | ||||
| -rw-r--r-- | public/javascripts/cacycle_diff.js | 1112 | ||||
| -rw-r--r-- | public/javascripts/related_assets.js | 80 | ||||
| -rw-r--r-- | public/stylesheets/admin.css | 1150 | ||||
| -rw-r--r-- | public/stylesheets/ccc.css | 24 |
6 files changed, 1418 insertions, 1594 deletions
diff --git a/public/javascripts/admin_interface.js b/public/javascripts/admin_interface.js index 450438f..e3d4ff1 100644 --- a/public/javascripts/admin_interface.js +++ b/public/javascripts/admin_interface.js | |||
| @@ -12,7 +12,7 @@ function hide_all() { | |||
| 12 | 12 | ||
| 13 | $(document).ready(function () { | 13 | $(document).ready(function () { |
| 14 | admin_search.initialize(); | 14 | admin_search.initialize(); |
| 15 | 15 | ||
| 16 | tinymce.init({ | 16 | tinymce.init({ |
| 17 | selector: 'textarea.with_editor', | 17 | selector: 'textarea.with_editor', |
| 18 | license_key: 'gpl', | 18 | license_key: 'gpl', |
| @@ -29,40 +29,56 @@ $(document).ready(function () { | |||
| 29 | cccms.setup_autosave(); | 29 | cccms.setup_autosave(); |
| 30 | }); | 30 | }); |
| 31 | } | 31 | } |
| 32 | }); | 32 | }); |
| 33 | |||
| 34 | if ($("#dashboard_search_term").length != 0) { | ||
| 35 | dashboard_search.initialize(); | ||
| 36 | } | ||
| 33 | 37 | ||
| 34 | if ($("#menu_search_term").length != 0) { | 38 | if ($("#menu_search_term").length != 0) { |
| 35 | menu_items.initialize_search(); | 39 | menu_items.initialize_search(); |
| 36 | } | 40 | } |
| 37 | 41 | ||
| 38 | if ($("#menu_item_list").length != 0) { | 42 | if ($("#menu_item_list").length != 0) { |
| 39 | menu_item_sorter.initialize(); | 43 | menu_item_sorter.initialize(); |
| 40 | } | 44 | } |
| 41 | 45 | ||
| 42 | if ($("#metadata").length != 0) { | 46 | if ($("#metadata").length != 0) { |
| 43 | meta_data.initialize(); | 47 | meta_data.initialize(); |
| 44 | } | 48 | } |
| 45 | 49 | ||
| 46 | if ($("#parent_search_term").length != 0) { | 50 | if ($("#parent_search_term").length != 0) { |
| 47 | parent_search.initialize_search(); | 51 | parent_search.initialize_search(); |
| 48 | } | 52 | } |
| 49 | 53 | ||
| 50 | if ($("#move_to_search_term").length != 0) { | 54 | if ($("#move_to_search_term").length != 0) { |
| 51 | move_to_search.initialize_search(); | 55 | move_to_search.initialize_search(); |
| 52 | } | 56 | } |
| 53 | 57 | ||
| 58 | if ($("#event_node_search_term").length != 0) { | ||
| 59 | event_search.initialize_search(); | ||
| 60 | } | ||
| 61 | |||
| 62 | if ($("#related_asset_search_term").length != 0) { | ||
| 63 | related_assets.initialize(); | ||
| 64 | } | ||
| 65 | |||
| 66 | if ($("#rrule_builder").length != 0) { | ||
| 67 | rrule_builder.initialize(); | ||
| 68 | } | ||
| 69 | |||
| 54 | if ($('#recent_changes_toggle').length != 0) { | 70 | if ($('#recent_changes_toggle').length != 0) { |
| 55 | hide_all(); | 71 | hide_all(); |
| 56 | $('#recent_changes_toggle').attr("class", "selected"); | 72 | $('#recent_changes_toggle').attr("class", "selected"); |
| 57 | $('#recent_changes_table').show(); | 73 | $('#recent_changes_table').show(); |
| 58 | 74 | ||
| 59 | $('#recent_changes_toggle').bind("click", function(){ | 75 | $('#recent_changes_toggle').bind("click", function(){ |
| 60 | hide_all(); | 76 | hide_all(); |
| 61 | $('#recent_changes_toggle').attr("class", "selected"); | 77 | $('#recent_changes_toggle').attr("class", "selected"); |
| 62 | $('#recent_changes_table').show(); | 78 | $('#recent_changes_table').show(); |
| 63 | return false; | 79 | return false; |
| 64 | }); | 80 | }); |
| 65 | 81 | ||
| 66 | $('#my_work_toggle').bind("click", function(){ | 82 | $('#my_work_toggle').bind("click", function(){ |
| 67 | hide_all(); | 83 | hide_all(); |
| 68 | $('#my_work_toggle').attr("class", "selected"); | 84 | $('#my_work_toggle').attr("class", "selected"); |
| @@ -76,7 +92,7 @@ $(document).ready(function () { | |||
| 76 | $('#my_work_table').show(); | 92 | $('#my_work_table').show(); |
| 77 | return false; | 93 | return false; |
| 78 | }); | 94 | }); |
| 79 | 95 | ||
| 80 | $('#current_drafts_toggle').bind("click", function(){ | 96 | $('#current_drafts_toggle').bind("click", function(){ |
| 81 | hide_all(); | 97 | hide_all(); |
| 82 | $('#current_drafts_toggle').attr("class", "selected"); | 98 | $('#current_drafts_toggle').attr("class", "selected"); |
| @@ -98,91 +114,108 @@ $(document).ready(function () { | |||
| 98 | return false; | 114 | return false; |
| 99 | }); | 115 | }); |
| 100 | } | 116 | } |
| 101 | 117 | ||
| 102 | jQuery.ajaxSetup({ | 118 | jQuery.ajaxSetup({ |
| 103 | 'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript");} | 119 | 'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript");} |
| 104 | }); | 120 | }); |
| 105 | 121 | ||
| 106 | $(document).ajaxSend(function(event, request, settings) { | 122 | $(document).ajaxSend(function(event, request, settings) { |
| 107 | if (typeof(AUTH_TOKEN) == "undefined") return; | 123 | if (typeof(AUTH_TOKEN) == "undefined") return; |
| 108 | // settings.data is a serialized string like "foo=bar&baz=boink" (or null) | 124 | // settings.data is a serialized string like "foo=bar&baz=boink" (or null) |
| 109 | settings.data = settings.data || ""; | 125 | settings.data = settings.data || ""; |
| 110 | settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN); | 126 | settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN); |
| 111 | }); | 127 | }); |
| 112 | 128 | ||
| 113 | }); | 129 | }); |
| 114 | 130 | ||
| 115 | 131 | ||
| 116 | meta_data = { | 132 | meta_data = { |
| 117 | initialize : function() { | 133 | initialize : function() { |
| 118 | $("#metadata").hide(); | 134 | document.getElementById("metadata_details").addEventListener("toggle", function() { |
| 119 | 135 | if (this.open) image_interface.initialize(); | |
| 120 | $("#button").click(function () { | ||
| 121 | |||
| 122 | $("#metadata").slideToggle(1200); | ||
| 123 | image_interface.initialize(); | ||
| 124 | |||
| 125 | if ($("#button").attr("class") == "unselected") { | ||
| 126 | $("#button").attr("class", "selected"); | ||
| 127 | |||
| 128 | } | ||
| 129 | else { | ||
| 130 | $("#button").attr("class", "unselected"); | ||
| 131 | $("#image_browser").hide(); | ||
| 132 | $("#image_browser_toggle").attr("class", "unselected"); | ||
| 133 | } | ||
| 134 | |||
| 135 | return false; | ||
| 136 | }); | 136 | }); |
| 137 | } | 137 | } |
| 138 | }; | 138 | }; |
| 139 | 139 | ||
| 140 | cccms = { | 140 | cccms = { |
| 141 | setup_autosave : function() { | 141 | setup_autosave : function() { |
| 142 | 142 | ||
| 143 | var elements = { | 143 | var elements = { |
| 144 | title : $('#page_title'), | 144 | title : $('#page_title'), |
| 145 | abstract : $('#page_abstract'), | 145 | abstract : $('#page_abstract'), |
| 146 | body : $('#page_body_ifr').contents().find('#tinymce'), | 146 | body : $('#page_body_ifr').contents().find('#tinymce'), |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | var page = { | 149 | var page = { |
| 150 | cached_title : elements.title.val(), | 150 | cached_title : elements.title.val(), |
| 151 | cached_abstract : elements.abstract.val(), | 151 | cached_abstract : elements.abstract.val(), |
| 152 | cached_body : elements.body.html(), | 152 | cached_body : elements.body.html(), |
| 153 | 153 | ||
| 154 | title_has_changed : function() { | 154 | title_has_changed : function() { |
| 155 | return (elements.title.val() != this.cached_title) | 155 | return (elements.title.val() != this.cached_title) |
| 156 | }, | 156 | }, |
| 157 | 157 | ||
| 158 | abstract_has_changed : function() { | 158 | abstract_has_changed : function() { |
| 159 | return (elements.abstract.val() != this.cached_abstract) | 159 | return (elements.abstract.val() != this.cached_abstract) |
| 160 | }, | 160 | }, |
| 161 | 161 | ||
| 162 | body_has_changed : function() { | 162 | body_has_changed : function() { |
| 163 | return elements.body.html() != this.cached_body | 163 | return elements.body.html() != this.cached_body |
| 164 | } | 164 | } |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | jQuery.fn.submitWithAjax = function(options) { | 167 | jQuery.fn.submitWithAjax = function(options) { |
| 168 | if (page.title_has_changed() || page.abstract_has_changed() || page.body_has_changed()) { | 168 | if (page.title_has_changed() || page.abstract_has_changed() || page.body_has_changed()) { |
| 169 | 169 | ||
| 170 | tinymce.triggerSave(); | ||
| 170 | page.cached_title = elements.title.val(); | 171 | page.cached_title = elements.title.val(); |
| 171 | page.cached_abstract = elements.abstract.val(); | 172 | page.cached_abstract = elements.abstract.val(); |
| 172 | page.cached_body = elements.body.html(); | 173 | page.cached_body = elements.body.html(); |
| 173 | 174 | ||
| 174 | $("#flash").append("<img src='/images/ajax-loader.gif' alt='' />"); | 175 | var data = this.serializeArray().filter(function(field) { |
| 175 | $.post(this.attr("action"), $(this).serialize(), null, "script"); | 176 | return field.name !== "_method"; |
| 176 | 177 | }); | |
| 178 | data.push({ name: "_method", value: "put" }); | ||
| 179 | |||
| 180 | $.ajax({ | ||
| 181 | type: "POST", | ||
| 182 | url: this.attr("data-autosave-url"), | ||
| 183 | data: data, | ||
| 184 | error: function(xhr) { | ||
| 185 | if (xhr.status === 423) { | ||
| 186 | clearInterval(cccms.autosave_timer); | ||
| 187 | cccms.report_lock_lost($("#page_editor > form").attr("data-show-url")); | ||
| 188 | } | ||
| 189 | // any other failure: quietly retried on the next tick | ||
| 190 | } | ||
| 191 | }); | ||
| 192 | |||
| 177 | } | 193 | } |
| 178 | }; | 194 | }; |
| 179 | 195 | ||
| 180 | setInterval('$("#page_editor > form").submitWithAjax()', 7000); | 196 | cccms.autosave_timer = setInterval(function() { |
| 197 | $("#page_editor > form").submitWithAjax(); | ||
| 198 | }, 7000); | ||
| 199 | }, | ||
| 200 | |||
| 201 | report_lock_lost : function(show_url) { | ||
| 202 | var $flash = $("#flash"); | ||
| 203 | if ($flash.length === 0) { | ||
| 204 | $flash = $("<div>", { id: "flash" }).insertAfter(".admin_content_spacer"); | ||
| 205 | } | ||
| 206 | |||
| 207 | var $banner = $("<span>", { "class": "warning" }).text( | ||
| 208 | "This page is now locked by someone else — your changes here can no longer be saved. Copy anything you need, then " | ||
| 209 | ); | ||
| 210 | $banner.append($("<a>", { href: show_url }).text("return to the published page")); | ||
| 211 | $banner.append("."); | ||
| 212 | |||
| 213 | $flash.html($banner); | ||
| 181 | } | 214 | } |
| 182 | } | 215 | } |
| 183 | 216 | ||
| 184 | menu_item_sorter = { | 217 | menu_item_sorter = { |
| 185 | 218 | ||
| 186 | initialize : function() { | 219 | initialize : function() { |
| 187 | $("#menu_item_list").sortable({ | 220 | $("#menu_item_list").sortable({ |
| 188 | axis: 'y', | 221 | axis: 'y', |
| @@ -205,24 +238,24 @@ menu_item_sorter = { | |||
| 205 | } | 238 | } |
| 206 | }); | 239 | }); |
| 207 | }, | 240 | }, |
| 208 | 241 | ||
| 209 | placeholder_helper : function(e,ui) { | 242 | placeholder_helper : function(e,ui) { |
| 210 | $(".ui-state-highlight").html("<td colspan='100%'></td>"); | 243 | $(".ui-state-highlight").html("<td colspan='100%'></td>"); |
| 211 | } | 244 | } |
| 212 | } | 245 | } |
| 213 | 246 | ||
| 214 | image_interface = { | 247 | image_interface = { |
| 215 | 248 | ||
| 216 | initialize : function() { | 249 | initialize : function() { |
| 217 | 250 | ||
| 218 | $("#image_browser").hide(); | 251 | $("#image_browser").hide(); |
| 219 | image_interface.initialize_sortable_image_box(); | 252 | image_interface.initialize_sortable_image_box(); |
| 220 | image_interface.connect_browser_and_box(); | 253 | image_interface.connect_browser_and_box(); |
| 221 | image_interface.set_droppable_behavior(); | 254 | image_interface.set_droppable_behavior(); |
| 222 | image_interface.bind_image_browser_toggle(); | 255 | image_interface.bind_image_browser_toggle(); |
| 223 | }, | 256 | }, |
| 224 | 257 | ||
| 225 | 258 | ||
| 226 | set_droppable_behavior : function() { | 259 | set_droppable_behavior : function() { |
| 227 | $("ul#image_box").droppable({ | 260 | $("ul#image_box").droppable({ |
| 228 | out : function(event, ui) { | 261 | out : function(event, ui) { |
| @@ -238,7 +271,7 @@ image_interface = { | |||
| 238 | } | 271 | } |
| 239 | }); | 272 | }); |
| 240 | }, | 273 | }, |
| 241 | 274 | ||
| 242 | connect_browser_and_box : function() { | 275 | connect_browser_and_box : function() { |
| 243 | $("#image_browser ul li").draggable({ | 276 | $("#image_browser ul li").draggable({ |
| 244 | connectToSortable : 'ul#image_box', | 277 | connectToSortable : 'ul#image_box', |
| @@ -246,9 +279,9 @@ image_interface = { | |||
| 246 | revert : 'invalid' | 279 | revert : 'invalid' |
| 247 | }); | 280 | }); |
| 248 | }, | 281 | }, |
| 249 | 282 | ||
| 250 | initialize_sortable_image_box : function() { | 283 | initialize_sortable_image_box : function() { |
| 251 | 284 | ||
| 252 | $("ul#image_box").sortable({ | 285 | $("ul#image_box").sortable({ |
| 253 | revert : true, | 286 | revert : true, |
| 254 | update : function(event, ui) { | 287 | update : function(event, ui) { |
| @@ -263,9 +296,9 @@ image_interface = { | |||
| 263 | } | 296 | } |
| 264 | }); | 297 | }); |
| 265 | } | 298 | } |
| 266 | }); | 299 | }); |
| 267 | }, | 300 | }, |
| 268 | 301 | ||
| 269 | bind_image_browser_toggle : function() { | 302 | bind_image_browser_toggle : function() { |
| 270 | $("#image_browser_toggle").bind("click", function(){ | 303 | $("#image_browser_toggle").bind("click", function(){ |
| 271 | if ($("#image_browser_toggle").attr("class") == "unselected") { | 304 | if ($("#image_browser_toggle").attr("class") == "unselected") { |
| @@ -276,10 +309,110 @@ image_interface = { | |||
| 276 | $("#image_browser_toggle").attr("class", "unselected"); | 309 | $("#image_browser_toggle").attr("class", "unselected"); |
| 277 | $("#image_browser").hide(); | 310 | $("#image_browser").hide(); |
| 278 | } | 311 | } |
| 279 | 312 | ||
| 280 | return false; | 313 | return false; |
| 281 | }); | 314 | }); |
| 282 | } | 315 | } |
| 283 | } | 316 | } |
| 284 | |||
| 285 | 317 | ||
| 318 | rrule_builder = { | ||
| 319 | initialize : function() { | ||
| 320 | rrule_builder.try_populate_from_rrule($("#event_rrule").val()); | ||
| 321 | |||
| 322 | $("input[name='rrule_freq']").bind("change", function() { | ||
| 323 | if ($(this).val() === "weekly") { rrule_builder.show_weekly_options(); } | ||
| 324 | else { rrule_builder.show_monthly_options(); } | ||
| 325 | rrule_builder.sync(); | ||
| 326 | }); | ||
| 327 | $("#rrule_monthly_ordinal").bind("change", function() { | ||
| 328 | $("#rrule_ordinal_fields").toggle($(this).is(":checked")); | ||
| 329 | rrule_builder.sync(); | ||
| 330 | }); | ||
| 331 | $("#rrule_exclude_month").bind("change", function() { | ||
| 332 | $("#rrule_excluded_month").toggle($(this).is(":checked")); | ||
| 333 | rrule_builder.sync(); | ||
| 334 | }); | ||
| 335 | $("#rrule_builder input, #rrule_builder select").bind("change", rrule_builder.sync); | ||
| 336 | }, | ||
| 337 | |||
| 338 | show_weekly_options : function() { | ||
| 339 | $("#rrule_weekly_options").show(); | ||
| 340 | $("#rrule_monthly_options").hide(); | ||
| 341 | }, | ||
| 342 | show_monthly_options : function() { | ||
| 343 | $("#rrule_weekly_options").hide(); | ||
| 344 | $("#rrule_monthly_options").show(); | ||
| 345 | }, | ||
| 346 | |||
| 347 | sync : function() { | ||
| 348 | var freq = $("input[name='rrule_freq']:checked").val(); | ||
| 349 | var parts = []; | ||
| 350 | |||
| 351 | if (freq === "weekly") { | ||
| 352 | parts.push("FREQ=WEEKLY"); | ||
| 353 | if ($("#rrule_biweekly").is(":checked")) parts.push("INTERVAL=2"); | ||
| 354 | var days = []; | ||
| 355 | $("input[id^='rrule_byday_']:checked").each(function() { | ||
| 356 | days.push($(this).attr("id").replace("rrule_byday_", "")); | ||
| 357 | }); | ||
| 358 | if (days.length > 0) parts.push("BYDAY=" + days.join(",")); | ||
| 359 | } else { | ||
| 360 | parts.push("FREQ=MONTHLY"); | ||
| 361 | if ($("#rrule_monthly_ordinal").is(":checked")) { | ||
| 362 | parts.push("BYDAY=" + $("#rrule_ordinal").val() + $("#rrule_ordinal_day").val()); | ||
| 363 | } | ||
| 364 | } | ||
| 365 | |||
| 366 | if ($("#rrule_exclude_month").is(":checked")) { | ||
| 367 | var excluded = parseInt($("#rrule_excluded_month").val(), 10); | ||
| 368 | var months = []; | ||
| 369 | for (var m = 1; m <= 12; m++) { if (m !== excluded) months.push(m); } | ||
| 370 | parts.push("BYMONTH=" + months.join(",")); | ||
| 371 | } | ||
| 372 | |||
| 373 | $("#event_rrule").val(parts.join(";")); | ||
| 374 | }, | ||
| 375 | |||
| 376 | try_populate_from_rrule : function(rrule) { | ||
| 377 | if (!rrule) return; | ||
| 378 | var parts = {}; | ||
| 379 | rrule.split(";").forEach(function(p) { | ||
| 380 | var kv = p.split("="); | ||
| 381 | parts[kv[0]] = kv[1]; | ||
| 382 | }); | ||
| 383 | if (parts.COUNT || parts.UNTIL) return; | ||
| 384 | |||
| 385 | if (parts.FREQ === "WEEKLY") { | ||
| 386 | $("input[name='rrule_freq'][value='weekly']").prop("checked", true); | ||
| 387 | rrule_builder.show_weekly_options(); | ||
| 388 | if (parts.INTERVAL === "2") $("#rrule_biweekly").prop("checked", true); | ||
| 389 | if (parts.BYDAY) { | ||
| 390 | parts.BYDAY.split(",").forEach(function(code) { | ||
| 391 | $("#rrule_byday_" + code).prop("checked", true); | ||
| 392 | }); | ||
| 393 | } | ||
| 394 | } else if (parts.FREQ === "MONTHLY") { | ||
| 395 | $("input[name='rrule_freq'][value='monthly']").prop("checked", true); | ||
| 396 | rrule_builder.show_monthly_options(); | ||
| 397 | var match = parts.BYDAY && parts.BYDAY.match(/^(-?\d+)([A-Z]{2})$/); | ||
| 398 | if (match) { | ||
| 399 | $("#rrule_monthly_ordinal").prop("checked", true); | ||
| 400 | $("#rrule_ordinal_fields").show(); | ||
| 401 | $("#rrule_ordinal").val(match[1]); | ||
| 402 | $("#rrule_ordinal_day").val(match[2]); | ||
| 403 | } | ||
| 404 | } else { | ||
| 405 | return; | ||
| 406 | } | ||
| 407 | |||
| 408 | if (parts.BYMONTH) { | ||
| 409 | var included = parts.BYMONTH.split(",").map(function(s) { return parseInt(s, 10); }); | ||
| 410 | var missing = []; | ||
| 411 | for (var m = 1; m <= 12; m++) { if (included.indexOf(m) === -1) missing.push(m); } | ||
| 412 | if (missing.length === 1) { | ||
| 413 | $("#rrule_exclude_month").prop("checked", true); | ||
| 414 | $("#rrule_excluded_month").val(missing[0]).show(); | ||
| 415 | } | ||
| 416 | } | ||
| 417 | } | ||
| 418 | }; | ||
diff --git a/public/javascripts/admin_search.js b/public/javascripts/admin_search.js index 9bf878b..7604bb7 100644 --- a/public/javascripts/admin_search.js +++ b/public/javascripts/admin_search.js | |||
| @@ -1,5 +1,4 @@ | |||
| 1 | admin_search = { | 1 | admin_search = { |
| 2 | |||
| 3 | initialize : function() { | 2 | initialize : function() { |
| 4 | $(document).bind("keydown", 'Alt+f', function(){ | 3 | $(document).bind("keydown", 'Alt+f', function(){ |
| 5 | admin_search.display_toggle(); | 4 | admin_search.display_toggle(); |
| @@ -20,25 +19,12 @@ admin_search = { | |||
| 20 | } | 19 | } |
| 21 | }); | 20 | }); |
| 22 | 21 | ||
| 23 | $("#search_term").bind("input", function() { | 22 | initSearchPicker({ |
| 24 | if (!$('#search_widget').is(':visible')) return; | 23 | inputSelector: "#search_term", |
| 25 | if ($(this).val()) { | 24 | resultsSelector: "#menu_search_results", |
| 26 | $.ajax({ | 25 | url: ADMIN_SEARCH_URL, |
| 27 | type: "GET", | 26 | isActive: function() { return $('#search_widget').is(':visible'); }, |
| 28 | url: ADMIN_SEARCH_URL, | 27 | resultsHeaderHtml: "<p class='search_more'>Press Enter to see all results ⏎</p>" |
| 29 | data: "search_term=" + $(this).val(), | ||
| 30 | dataType: "json", | ||
| 31 | success: function(results) { | ||
| 32 | admin_search.show_results(results); | ||
| 33 | }, | ||
| 34 | error: function(xhr, status, error) { | ||
| 35 | console.log("Ajax error:", status, error, xhr.status, xhr.responseText); | ||
| 36 | } | ||
| 37 | }); | ||
| 38 | } else { | ||
| 39 | $('#search_results').slideUp(); | ||
| 40 | $('#search_results').empty(); | ||
| 41 | } | ||
| 42 | }); | 28 | }); |
| 43 | }, | 29 | }, |
| 44 | 30 | ||
| @@ -49,75 +35,155 @@ admin_search = { | |||
| 49 | $('#search_widget').fadeIn(); | 35 | $('#search_widget').fadeIn(); |
| 50 | $('#search_term').focus(); | 36 | $('#search_term').focus(); |
| 51 | } | 37 | } |
| 52 | }, | ||
| 53 | |||
| 54 | show_results : function(results) { | ||
| 55 | $('#search_results').empty(); | ||
| 56 | if (results.length) { | ||
| 57 | $('#search_results').append( | ||
| 58 | "<p class='search_more'>Press Enter to see all results ⏎</p>" | ||
| 59 | ); | ||
| 60 | } | ||
| 61 | for (result in results) { | ||
| 62 | $('#search_results').append( | ||
| 63 | "<p><a href='" + results[result].node_path + "'>" + | ||
| 64 | results[result].title + | ||
| 65 | "<span class='result_path'>" + results[result].unique_name + "</span>" + | ||
| 66 | "</a></p>" | ||
| 67 | ); | ||
| 68 | } | ||
| 69 | $('#search_results').slideDown(); | ||
| 70 | } | 38 | } |
| 71 | }; | 39 | }; |
| 72 | 40 | ||
| 73 | menu_items = { | 41 | function initSearchPicker(options) { |
| 42 | var inputSelector = options.inputSelector; | ||
| 43 | var resultsSelector = options.resultsSelector; | ||
| 44 | var url = options.url || ADMIN_MENU_SEARCH_URL; | ||
| 45 | var onSelect = options.onSelect; | ||
| 46 | var isActive = options.isActive; | ||
| 47 | var resultsHeaderHtml = options.resultsHeaderHtml; | ||
| 48 | var renderResults = options.renderResults; | ||
| 49 | var loadOnFocus = options.loadOnFocus; // optional, fires an initial search on focus with no term typed yet | ||
| 50 | var requestId = 0; | ||
| 51 | var timeout; | ||
| 52 | var results = $(resultsSelector); | ||
| 74 | 53 | ||
| 75 | initialize_search : function() { | 54 | function runSearch(term) { |
| 76 | $("#menu_search_term").bind("keyup", function() { | 55 | var thisRequest = ++requestId; |
| 77 | if ($(this).attr("value")) { | 56 | $.ajax({ |
| 78 | $.ajax({ | 57 | type: "GET", |
| 79 | type: "GET", | 58 | url: url, |
| 80 | url: ADMIN_MENU_SEARCH_URL, | 59 | data: "search_term=" + term, |
| 81 | data: "search_term=" + $(this).attr("value"), | 60 | dataType: "json", |
| 82 | dataType: "json", | 61 | success: function(data) { |
| 83 | success : function(results) { | 62 | if (thisRequest !== requestId) return; |
| 84 | menu_items.show_results(results); | 63 | results.empty(); |
| 64 | |||
| 65 | var found; | ||
| 66 | if (renderResults) { | ||
| 67 | found = renderResults(data, results, resultsHeaderHtml); | ||
| 68 | } else { | ||
| 69 | if (data.length && resultsHeaderHtml) { | ||
| 70 | results.append(resultsHeaderHtml); | ||
| 85 | } | 71 | } |
| 86 | }); | 72 | found = false; |
| 87 | } | 73 | for (var i = 0; i < data.length; i++) { |
| 88 | else { | 74 | (function(node) { |
| 89 | $('#search_results').slideUp(); | 75 | var link; |
| 90 | $('#search_results').empty(); | 76 | if (onSelect) { |
| 77 | link = $( | ||
| 78 | "<p><a href='#'>" + node.title + | ||
| 79 | "<span class='result_path'>" + node.unique_name + "</span></a></p>" | ||
| 80 | ); | ||
| 81 | link.find("a").bind("click", function() { | ||
| 82 | onSelect(node); | ||
| 83 | results.slideUp(); | ||
| 84 | results.empty(); | ||
| 85 | return false; | ||
| 86 | }); | ||
| 87 | } else { | ||
| 88 | link = $( | ||
| 89 | "<p><a href='" + node.node_path + "'>" + node.title + | ||
| 90 | "<span class='result_path'>" + node.unique_name + "</span></a></p>" | ||
| 91 | ); | ||
| 92 | } | ||
| 93 | results.append(link); | ||
| 94 | found = true; | ||
| 95 | })(data[i]); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | if (found) { | ||
| 100 | results.slideDown(); | ||
| 101 | } else { | ||
| 102 | results.slideUp(); | ||
| 103 | } | ||
| 104 | }, | ||
| 105 | error: function(xhr, status, error) { | ||
| 106 | console.log("Ajax error:", status, error, xhr.status, xhr.responseText); | ||
| 91 | } | 107 | } |
| 92 | }); | 108 | }); |
| 93 | }, | 109 | } |
| 94 | 110 | ||
| 95 | show_results : function(results) { | 111 | $(inputSelector).bind("input", function() { |
| 96 | $("#search_results").empty(); | 112 | if (isActive && !isActive()) return; |
| 97 | for (result in results) { | ||
| 98 | var link = $(("<a href='#'>"+ results[result].title + "</a>")); | ||
| 99 | $(link).bind("click", menu_items.link_closure(results[result])); | ||
| 100 | 113 | ||
| 114 | var term = $(this).val(); | ||
| 115 | clearTimeout(timeout); | ||
| 101 | 116 | ||
| 102 | // Sometimes I don't get jquery; wrap() didn't work *sigh* | 117 | if (!term) { |
| 103 | // Guess I'll need a book someday or another framework | 118 | results.slideUp(); |
| 104 | var wrapper = $("<div></div>"); | 119 | results.empty(); |
| 105 | $(wrapper).append(link) | 120 | return; |
| 121 | } | ||
| 106 | 122 | ||
| 107 | $("#search_results").append(wrapper); | 123 | timeout = setTimeout(function() { runSearch(term); }, 250); |
| 124 | }); | ||
| 108 | 125 | ||
| 109 | } | 126 | if (loadOnFocus) { |
| 110 | }, | 127 | $(inputSelector).bind("focus", function() { |
| 128 | if (isActive && !isActive()) return; | ||
| 129 | if ($(this).val()) return; // the input handler above already covers a real term | ||
| 130 | runSearch(""); | ||
| 131 | }); | ||
| 132 | } | ||
| 133 | } | ||
| 111 | 134 | ||
| 112 | link_closure : function(node) { | 135 | dashboard_search = { |
| 113 | var barf = function(){ | 136 | initialize : function() { |
| 114 | $("#menu_item_node_id").val(node.node_id); | 137 | initSearchPicker({ |
| 115 | $("#menu_item_path").val("/" + node.unique_name); | 138 | inputSelector: "#dashboard_search_term", |
| 116 | $("#menu_item_title").val(node.title); | 139 | resultsSelector: "#dashboard_search_results", |
| 117 | return false; | 140 | url: DASHBOARD_SEARCH_URL, |
| 118 | } | 141 | resultsHeaderHtml: "<p class='search_more'>Press Enter to see all results ⏎</p>", |
| 142 | renderResults: function(data, results, resultsHeaderHtml) { | ||
| 143 | var found = false; | ||
| 144 | |||
| 145 | if ((data.tags.length || data.nodes.length) && resultsHeaderHtml) { | ||
| 146 | results.append(resultsHeaderHtml); | ||
| 147 | } | ||
| 148 | |||
| 149 | if (data.tags.length) { | ||
| 150 | results.append("<p class='search_group_label'>Tags</p>"); | ||
| 151 | var tag_row = $("<div class='search_tag_row'></div>"); | ||
| 152 | data.tags.forEach(function(tag) { | ||
| 153 | tag_row.append("<a class='search_tag_pill' href='" + tag.tag_path + "'>" + tag.name + "</a>"); | ||
| 154 | found = true; | ||
| 155 | }); | ||
| 156 | results.append(tag_row); | ||
| 157 | } | ||
| 158 | |||
| 159 | if (data.nodes.length) { | ||
| 160 | results.append("<p class='search_group_label'>Pages</p>"); | ||
| 161 | data.nodes.forEach(function(node) { | ||
| 162 | results.append( | ||
| 163 | "<p><a href='" + node.node_path + "'>" + node.title + | ||
| 164 | "<span class='result_path'>" + node.unique_name + "</span></a></p>" | ||
| 165 | ); | ||
| 166 | found = true; | ||
| 167 | }); | ||
| 168 | } | ||
| 169 | |||
| 170 | return found; | ||
| 171 | } | ||
| 172 | }); | ||
| 173 | } | ||
| 174 | }; | ||
| 119 | 175 | ||
| 120 | return barf; | 176 | menu_items = { |
| 177 | initialize_search : function() { | ||
| 178 | initSearchPicker({ | ||
| 179 | inputSelector: "#menu_search_term", | ||
| 180 | resultsSelector: "#menu_item_search_results", | ||
| 181 | onSelect: function(node) { | ||
| 182 | $("#menu_item_node_id").val(node.node_id); | ||
| 183 | $("#menu_item_path").val("/" + node.unique_name); | ||
| 184 | $("#menu_item_title").val(node.title); | ||
| 185 | } | ||
| 186 | }); | ||
| 121 | } | 187 | } |
| 122 | }; | 188 | }; |
| 123 | 189 | ||
| @@ -125,136 +191,93 @@ parent_search = { | |||
| 125 | initialize_search : function() { | 191 | initialize_search : function() { |
| 126 | parent_search.initialize_radio_buttons(); | 192 | parent_search.initialize_radio_buttons(); |
| 127 | 193 | ||
| 128 | $("#parent_search_term").bind("keyup", function() { | 194 | initSearchPicker({ |
| 129 | if ($(this).attr("value")) { | 195 | inputSelector: "#parent_search_term", |
| 130 | $.ajax({ | 196 | resultsSelector: "#parent_search_results", |
| 131 | type: "GET", | 197 | onSelect: function(node) { |
| 132 | url: ADMIN_MENU_SEARCH_URL, | 198 | $("#parent_search_term").val(node.title); |
| 133 | data: "search_term=" + $(this).attr("value"), | 199 | $("#parent_id").val(node.node_id).attr("data-unique-name", node.unique_name); |
| 134 | dataType: "json", | 200 | parent_search.update_resulting_path(); |
| 135 | success : function(results) { | ||
| 136 | parent_search.show_results(results); | ||
| 137 | } | ||
| 138 | }); | ||
| 139 | } | ||
| 140 | else { | ||
| 141 | $('#search_results').slideUp(); | ||
| 142 | $('#search_results').empty(); | ||
| 143 | } | 201 | } |
| 144 | }); | 202 | }); |
| 145 | }, | ||
| 146 | 203 | ||
| 147 | show_results : function(results) { | 204 | $("#title").bind("input", function() { |
| 148 | $("#search_results").empty(); | 205 | parent_search.update_resulting_path(); |
| 149 | var found = false; | 206 | }); |
| 150 | for (result in results) { | ||
| 151 | var link = $(("<a href='#'>"+ results[result].title + "</a>")); | ||
| 152 | $(link).bind("click", parent_search.link_closure(results[result])); | ||
| 153 | 207 | ||
| 208 | $("#copy_resulting_path").bind("click", function() { | ||
| 209 | var path = $("#resulting_path").text(); | ||
| 210 | if (path === "—" || !navigator.clipboard) return; | ||
| 211 | navigator.clipboard.writeText(path); | ||
| 212 | }); | ||
| 213 | }, | ||
| 154 | 214 | ||
| 155 | // Sometimes I don't get jquery; wrap() didn't work *sigh* | 215 | update_resulting_path : function() { |
| 156 | // Guess I'll need a book someday or another framework | 216 | var kind = $("input[name='kind']:checked"); |
| 157 | var wrapper = $("<div></div>"); | 217 | var title = $("#title").val(); |
| 158 | $(wrapper).append(link); | ||
| 159 | 218 | ||
| 160 | $("#search_results").append(wrapper); | 219 | if (title === "") { |
| 161 | found = true; | 220 | $("#resulting_path").text("—"); |
| 221 | return; | ||
| 162 | } | 222 | } |
| 163 | if (found) | ||
| 164 | $('#search_results').slideDown(); | ||
| 165 | }, | ||
| 166 | 223 | ||
| 167 | link_closure : function(node) { | 224 | var prefix = kind.val() === "generic" |
| 168 | var barf = function(){ | 225 | ? ($("#parent_id").attr("data-unique-name") || "") |
| 169 | $("#parent_search_term").attr("value", node.title); | 226 | : (kind.attr("data-path-prefix") || ""); |
| 170 | $("#parent_id").attr("value", node.node_id); | ||
| 171 | $('#search_results').slideUp(); | ||
| 172 | $('#search_results').empty(); | ||
| 173 | return false; | ||
| 174 | } | ||
| 175 | 227 | ||
| 176 | return barf; | 228 | clearTimeout(parent_search.path_timeout); |
| 229 | parent_search.path_timeout = setTimeout(function() { | ||
| 230 | $.get(PARAMETERIZE_PREVIEW_URL, { title: title }, function(slug) { | ||
| 231 | $("#resulting_path").text(window.location.origin + "/" + (prefix ? prefix + "/" : "") + slug); | ||
| 232 | }); | ||
| 233 | }, 300); | ||
| 177 | }, | 234 | }, |
| 178 | 235 | ||
| 179 | initialize_radio_buttons : function() { | 236 | initialize_radio_buttons : function() { |
| 180 | $("#kind_top_level").bind("change", function(){ | 237 | parent_search.sync_parent_field(); |
| 181 | $("#parent_search_field").hide(); | 238 | $("input[name='kind']").bind("change", function() { |
| 182 | }); | 239 | parent_search.sync_parent_field(); |
| 183 | 240 | parent_search.update_resulting_path(); | |
| 184 | $("#kind_update").bind("change", function(){ | ||
| 185 | $("#parent_search_field").hide(); | ||
| 186 | }); | 241 | }); |
| 242 | }, | ||
| 187 | 243 | ||
| 188 | $("#kind_press_release").bind("change", function(){ | 244 | sync_parent_field : function() { |
| 245 | var kind = $("input[name='kind']:checked").val(); | ||
| 246 | if (kind === "generic") { | ||
| 247 | $("#parent_search_field").show(); | ||
| 248 | } else { | ||
| 189 | $("#parent_search_field").hide(); | 249 | $("#parent_search_field").hide(); |
| 190 | }); | 250 | } |
| 251 | } | ||
| 252 | }; | ||
| 191 | 253 | ||
| 192 | $("#kind_generic").bind("change", function(){ | 254 | move_to_search = { |
| 193 | $("#parent_search_field").show(); | 255 | initialize_search : function() { |
| 256 | initSearchPicker({ | ||
| 257 | inputSelector: "#move_to_search_term", | ||
| 258 | resultsSelector: "#move_to_search_results", | ||
| 259 | onSelect: function(node) { | ||
| 260 | $("#move_to_search_term").val(node.title); | ||
| 261 | $("#node_staged_parent_id").val(node.node_id); | ||
| 262 | } | ||
| 194 | }); | 263 | }); |
| 195 | |||
| 196 | } | 264 | } |
| 197 | } | 265 | }; |
| 198 | 266 | ||
| 199 | move_to_search = { | 267 | event_search = { |
| 200 | initialize_search : function() { | 268 | initialize_search : function() { |
| 201 | $("#move_to_search_term").bind("keyup", function() { move_to_search.do_search($(this))}); | 269 | initSearchPicker({ |
| 202 | $("#move_to_search_term").bind("keydown", function() { move_to_search.do_search($(this))}); | 270 | inputSelector: "#event_node_search_term", |
| 203 | $("#move_to_search_term").bind("keypress", function() { move_to_search.do_search($(this))}); | 271 | resultsSelector: "#event_search_results", |
| 204 | $("#move_to_search_term").bind("paste", function() { move_to_search.do_search($(this))}); | 272 | onSelect: function(node) { |
| 205 | $("#move_to_search_term").bind("cut", function() { move_to_search.do_search($(this))}); | 273 | $("#event_node_search_term").val(node.title); |
| 206 | }, | 274 | $("#event_node_id").val(node.node_id); |
| 207 | 275 | ||
| 208 | do_search : function(_this) { | 276 | var title_field = $("#event_title"); |
| 209 | if (_this.attr("value")) { | 277 | if (title_field.val() === "") { |
| 210 | $.ajax({ | 278 | $("#event_title_hint").text("Using \"" + node.title + "\" from the associated node."); |
| 211 | type: "GET", | ||
| 212 | url: ADMIN_MENU_SEARCH_URL, | ||
| 213 | data: "search_term=" + _this.attr("value"), | ||
| 214 | dataType: "json", | ||
| 215 | success : function(results) { | ||
| 216 | move_to_search.show_results(results); | ||
| 217 | } | 279 | } |
| 218 | }); | 280 | } |
| 219 | } | 281 | }); |
| 220 | else { | ||
| 221 | $('#search_results').slideUp(); | ||
| 222 | $('#search_results').empty(); | ||
| 223 | } | ||
| 224 | }, | ||
| 225 | |||
| 226 | show_results : function(results) { | ||
| 227 | $("#search_results").empty(); | ||
| 228 | var found = false; | ||
| 229 | for (result in results) { | ||
| 230 | var link = $(("<a href='#'>"+ results[result].title + "</a>")); | ||
| 231 | $(link).bind("click", move_to_search.link_closure(results[result])); | ||
| 232 | |||
| 233 | |||
| 234 | // Sometimes I don't get jquery; wrap() didn't work *sigh* | ||
| 235 | // Guess I'll need a book someday or another framework | ||
| 236 | var wrapper = $("<div></div>"); | ||
| 237 | $(wrapper).append(link) | ||
| 238 | |||
| 239 | $("#search_results").append(wrapper); | ||
| 240 | found = true; | ||
| 241 | } | ||
| 242 | if (found) | ||
| 243 | $('#search_results').slideDown(); | ||
| 244 | else | ||
| 245 | $('#search_results').slideUp(); | ||
| 246 | |||
| 247 | }, | ||
| 248 | |||
| 249 | link_closure : function(node) { | ||
| 250 | var barf = function(){ | ||
| 251 | $("#move_to_search_term").attr("value", node.title); | ||
| 252 | $("#node_staged_parent_id").attr("value", node.node_id); | ||
| 253 | $('#search_results').slideUp(); | ||
| 254 | $('#search_results').empty(); | ||
| 255 | return false; | ||
| 256 | } | ||
| 257 | |||
| 258 | return barf; | ||
| 259 | } | 282 | } |
| 260 | } | 283 | }; |
diff --git a/public/javascripts/cacycle_diff.js b/public/javascripts/cacycle_diff.js deleted file mode 100644 index 24f9d0b..0000000 --- a/public/javascripts/cacycle_diff.js +++ /dev/null | |||
| @@ -1,1112 +0,0 @@ | |||
| 1 | // <pre><nowiki> | ||
| 2 | |||
| 3 | /* | ||
| 4 | |||
| 5 | Name: diff.js | ||
| 6 | Version: 0.9.5a (April 6, 2008) | ||
| 7 | Info: http://en.wikipedia.org/wiki/User:Cacycle/diff | ||
| 8 | Code: http://en.wikipedia.org/wiki/User:Cacycle/diff.js | ||
| 9 | |||
| 10 | JavaScript diff algorithm by [[en:User:Cacycle]] (http://en.wikipedia.org/wiki/User_talk:Cacycle). | ||
| 11 | Outputs html/css-formatted new text with highlighted deletions, inserts, and block moves. | ||
| 12 | |||
| 13 | The program uses cross-browser code and should work with all modern browsers. It has been tested with: | ||
| 14 | * Mozilla Firefox 1.5.0.1 | ||
| 15 | * Mozilla SeaMonkey 1.0 | ||
| 16 | * Opera 8.53 | ||
| 17 | * Internet Explorer 6.0.2900.2180 | ||
| 18 | * Internet Explorer 7.0.5730.11 | ||
| 19 | This program is also compatibel with Greasemonkey | ||
| 20 | |||
| 21 | An implementation of the word-based algorithm from: | ||
| 22 | |||
| 23 | Communications of the ACM 21(4):264 (1978) | ||
| 24 | http://doi.acm.org/10.1145/359460.359467 | ||
| 25 | |||
| 26 | With the following additional feature: | ||
| 27 | |||
| 28 | * Word types have been optimized for MediaWiki source texts | ||
| 29 | * Additional post-pass 5 code for resolving islands caused by adding | ||
| 30 | two common words at the end of sequences of common words | ||
| 31 | * Additional detection of block borders and color coding of moved blocks and their original position | ||
| 32 | * Optional "intelligent" omission of unchanged parts from the output | ||
| 33 | |||
| 34 | This code is used by the MediaWiki in-browser text editors [[en:User:Cacycle/editor]] and [[en:User:Cacycle/wikEd]] | ||
| 35 | and the enhanced diff view tool wikEdDiff [[en:User:Cacycle/wikEd]]. | ||
| 36 | |||
| 37 | Usage: var htmlText = WDiffString(oldText, newText); | ||
| 38 | |||
| 39 | This code has been released into the public domain. | ||
| 40 | |||
| 41 | Datastructures: | ||
| 42 | |||
| 43 | text: an object that holds all text related datastructures | ||
| 44 | .newWords: consecutive words of the new text (N) | ||
| 45 | .oldWords: consecutive words of the old text (O) | ||
| 46 | .newToOld: array of corresponding word number in old text (NA) | ||
| 47 | .oldToNew: array of corresponding word number in new text (OA) | ||
| 48 | .message: output message for testing purposes | ||
| 49 | |||
| 50 | symbol['word']: symbol table for passes 1 - 3, holds words as a hash | ||
| 51 | .newCtr: new word occurences counter (NC) | ||
| 52 | .oldCtr: old word occurences counter (OC) | ||
| 53 | .toNew: table last old word number | ||
| 54 | .toOld: last new word number (OLNA) | ||
| 55 | |||
| 56 | block: an object that holds block move information | ||
| 57 | blocks indexed after new text: | ||
| 58 | .newStart: new text word number of start of this block | ||
| 59 | .newLength: element number of this block including non-words | ||
| 60 | .newWords: true word number of this block | ||
| 61 | .newNumber: corresponding block index in old text | ||
| 62 | .newBlock: moved-block-number of a block that has been moved here | ||
| 63 | .newLeft: moved-block-number of a block that has been moved from this border leftwards | ||
| 64 | .newRight: moved-block-number of a block that has been moved from this border rightwards | ||
| 65 | .newLeftIndex: index number of a block that has been moved from this border leftwards | ||
| 66 | .newRightIndex: index number of a block that has been moved from this border rightwards | ||
| 67 | blocks indexed after old text: | ||
| 68 | .oldStart: word number of start of this block | ||
| 69 | .oldToNew: corresponding new text word number of start | ||
| 70 | .oldLength: element number of this block including non-words | ||
| 71 | .oldWords: true word number of this block | ||
| 72 | |||
| 73 | */ | ||
| 74 | |||
| 75 | |||
| 76 | // css for change indicators | ||
| 77 | if (typeof(wDiffStyleDelete) == 'undefined') { window.wDiffStyleDelete = 'font-weight: normal; text-decoration: none; color: #fff; background-color: #990033;'; } | ||
| 78 | if (typeof(wDiffStyleInsert) == 'undefined') { window.wDiffStyleInsert = 'font-weight: normal; text-decoration: none; color: #fff; background-color: #009933;'; } | ||
| 79 | if (typeof(wDiffStyleMoved) == 'undefined') { window.wDiffStyleMoved = 'font-weight: bold; color: #000; vertical-align: text-bottom; font-size: xx-small; padding: 0; border: solid 1px;'; } | ||
| 80 | if (typeof(wDiffStyleBlock) == 'undefined') { window.wDiffStyleBlock = [ | ||
| 81 | 'color: #000; background-color: #ffff80;', | ||
| 82 | 'color: #000; background-color: #c0ffff;', | ||
| 83 | 'color: #000; background-color: #ffd0f0;', | ||
| 84 | 'color: #000; background-color: #ffe080;', | ||
| 85 | 'color: #000; background-color: #aaddff;', | ||
| 86 | 'color: #000; background-color: #ddaaff;', | ||
| 87 | 'color: #000; background-color: #ffbbbb;', | ||
| 88 | 'color: #000; background-color: #d8ffa0;', | ||
| 89 | 'color: #000; background-color: #d0d0d0;' | ||
| 90 | ]; } | ||
| 91 | |||
| 92 | // html for change indicators, {number} is replaced by the block number | ||
| 93 | // {block} is replaced by the block style, class and html comments are important for shortening the output | ||
| 94 | if (typeof(wDiffHtmlMovedRight) == 'undefined') { window.wDiffHtmlMovedRight = '<input class="wDiffHtmlMovedRight" type="button" value=">" style="' + wDiffStyleMoved + ' {block}"><!--wDiffHtmlMovedRight-->'; } | ||
| 95 | if (typeof(wDiffHtmlMovedLeft) == 'undefined') { window.wDiffHtmlMovedLeft = '<input class="wDiffHtmlMovedLeft" type="button" value="<" style="' + wDiffStyleMoved + ' {block}"><!--wDiffHtmlMovedLeft-->'; } | ||
| 96 | |||
| 97 | if (typeof(wDiffHtmlBlockStart) == 'undefined') { window.wDiffHtmlBlockStart = '<span class="wDiffHtmlBlock" style="{block}">'; } | ||
| 98 | if (typeof(wDiffHtmlBlockEnd) == 'undefined') { window.wDiffHtmlBlockEnd = '</span><!--wDiffHtmlBlock-->'; } | ||
| 99 | |||
| 100 | if (typeof(wDiffHtmlDeleteStart) == 'undefined') { window.wDiffHtmlDeleteStart = '<span class="wDiffHtmlDelete" style="' + wDiffStyleDelete + '">'; } | ||
| 101 | if (typeof(wDiffHtmlDeleteEnd) == 'undefined') { window.wDiffHtmlDeleteEnd = '</span><!--wDiffHtmlDelete-->'; } | ||
| 102 | |||
| 103 | if (typeof(wDiffHtmlInsertStart) == 'undefined') { window.wDiffHtmlInsertStart = '<span class="wDiffHtmlInsert" style="' + wDiffStyleInsert + '">'; } | ||
| 104 | if (typeof(wDiffHtmlInsertEnd) == 'undefined') { window.wDiffHtmlInsertEnd = '</span><!--wDiffHtmlInsert-->'; } | ||
| 105 | |||
| 106 | // minimal number of real words for a moved block (0 for always displaying block move indicators) | ||
| 107 | if (typeof(wDiffBlockMinLength) == 'undefined') { window.wDiffBlockMinLength = 3; } | ||
| 108 | |||
| 109 | // exclude identical sequence starts and endings from change marking | ||
| 110 | if (typeof(wDiffWordDiff) == 'undefined') { window.wDiffWordDiff = true; } | ||
| 111 | |||
| 112 | // enable recursive diff to resolve problematic sequences | ||
| 113 | if (typeof(wDiffRecursiveDiff) == 'undefined') { window.wDiffRecursiveDiff = true; } | ||
| 114 | |||
| 115 | // enable block move display | ||
| 116 | if (typeof(wDiffShowBlockMoves) == 'undefined') { window.wDiffShowBlockMoves = true; } | ||
| 117 | |||
| 118 | // remove unchanged parts from final output | ||
| 119 | |||
| 120 | // characters before diff tag to search for previous heading, paragraph, line break, cut characters | ||
| 121 | if (typeof(wDiffHeadingBefore) == 'undefined') { window.wDiffHeadingBefore = 1500; } | ||
| 122 | if (typeof(wDiffParagraphBefore) == 'undefined') { window.wDiffParagraphBefore = 1500; } | ||
| 123 | if (typeof(wDiffLineBeforeMax) == 'undefined') { window.wDiffLineBeforeMax = 1000; } | ||
| 124 | if (typeof(wDiffLineBeforeMin) == 'undefined') { window.wDiffLineBeforeMin = 500; } | ||
| 125 | if (typeof(wDiffBlankBeforeMax) == 'undefined') { window.wDiffBlankBeforeMax = 1000; } | ||
| 126 | if (typeof(wDiffBlankBeforeMin) == 'undefined') { window.wDiffBlankBeforeMin = 500; } | ||
| 127 | if (typeof(wDiffCharsBefore) == 'undefined') { window.wDiffCharsBefore = 500; } | ||
| 128 | |||
| 129 | // characters after diff tag to search for next heading, paragraph, line break, or characters | ||
| 130 | if (typeof(wDiffHeadingAfter) == 'undefined') { window.wDiffHeadingAfter = 1500; } | ||
| 131 | if (typeof(wDiffParagraphAfter) == 'undefined') { window.wDiffParagraphAfter = 1500; } | ||
| 132 | if (typeof(wDiffLineAfterMax) == 'undefined') { window.wDiffLineAfterMax = 1000; } | ||
| 133 | if (typeof(wDiffLineAfterMin) == 'undefined') { window.wDiffLineAfterMin = 500; } | ||
| 134 | if (typeof(wDiffBlankAfterMax) == 'undefined') { window.wDiffBlankAfterMax = 1000; } | ||
| 135 | if (typeof(wDiffBlankAfterMin) == 'undefined') { window.wDiffBlankAfterMin = 500; } | ||
| 136 | if (typeof(wDiffCharsAfter) == 'undefined') { window.wDiffCharsAfter = 500; } | ||
| 137 | |||
| 138 | // maximal fragment distance to join close fragments | ||
| 139 | if (typeof(wDiffFragmentJoin) == 'undefined') { window.wDiffFragmentJoin = 1000; } | ||
| 140 | if (typeof(wDiffOmittedChars) == 'undefined') { window.wDiffOmittedChars = '…'; } | ||
| 141 | if (typeof(wDiffOmittedLines) == 'undefined') { window.wDiffOmittedLines = '<hr style="height: 2px; margin: 1em 10%;">'; } | ||
| 142 | if (typeof(wDiffNoChange) == 'undefined') { window.wDiffNoChange = '<hr style="height: 2px; margin: 1em 20%;">'; } | ||
| 143 | |||
| 144 | // compatibility fix for old name of main function | ||
| 145 | window.StringDiff = window.WDiffString; | ||
| 146 | |||
| 147 | |||
| 148 | // WDiffString: main program | ||
| 149 | // input: oldText, newText, strings containing the texts | ||
| 150 | // returns: html diff | ||
| 151 | |||
| 152 | window.WDiffString = function(oldText, newText) { | ||
| 153 | |||
| 154 | // IE / Mac fix | ||
| 155 | oldText = oldText.replace(/(\r\n)/g, '\n'); | ||
| 156 | newText = newText.replace(/(\r\n)/g, '\n'); | ||
| 157 | |||
| 158 | var text = {}; | ||
| 159 | text.newWords = []; | ||
| 160 | text.oldWords = []; | ||
| 161 | text.newToOld = []; | ||
| 162 | text.oldToNew = []; | ||
| 163 | text.message = ''; | ||
| 164 | var block = {}; | ||
| 165 | var outText = ''; | ||
| 166 | |||
| 167 | // trap trivial changes: no change | ||
| 168 | if (oldText == newText) { | ||
| 169 | outText = newText; | ||
| 170 | outText = WDiffEscape(outText); | ||
| 171 | outText = WDiffHtmlFormat(outText); | ||
| 172 | return(outText); | ||
| 173 | } | ||
| 174 | |||
| 175 | // trap trivial changes: old text deleted | ||
| 176 | if ( (oldText == null) || (oldText.length == 0) ) { | ||
| 177 | outText = newText; | ||
| 178 | outText = WDiffEscape(outText); | ||
| 179 | outText = WDiffHtmlFormat(outText); | ||
| 180 | outText = wDiffHtmlInsertStart + outText + wDiffHtmlInsertEnd; | ||
| 181 | return(outText); | ||
| 182 | } | ||
| 183 | |||
| 184 | // trap trivial changes: new text deleted | ||
| 185 | if ( (newText == null) || (newText.length == 0) ) { | ||
| 186 | outText = oldText; | ||
| 187 | outText = WDiffEscape(outText); | ||
| 188 | outText = WDiffHtmlFormat(outText); | ||
| 189 | outText = wDiffHtmlDeleteStart + outText + wDiffHtmlDeleteEnd; | ||
| 190 | return(outText); | ||
| 191 | } | ||
| 192 | |||
| 193 | // split new and old text into words | ||
| 194 | WDiffSplitText(oldText, newText, text); | ||
| 195 | |||
| 196 | // calculate diff information | ||
| 197 | WDiffText(text); | ||
| 198 | |||
| 199 | //detect block borders and moved blocks | ||
| 200 | WDiffDetectBlocks(text, block); | ||
| 201 | |||
| 202 | // process diff data into formatted html text | ||
| 203 | outText = WDiffToHtml(text, block); | ||
| 204 | |||
| 205 | // IE fix | ||
| 206 | outText = outText.replace(/> ( *)</g, '> $1<'); | ||
| 207 | |||
| 208 | return(outText); | ||
| 209 | } | ||
| 210 | |||
| 211 | |||
| 212 | // WDiffSplitText: split new and old text into words | ||
| 213 | // input: oldText, newText, strings containing the texts | ||
| 214 | // changes: text.newWords and text.oldWords, arrays containing the texts in arrays of words | ||
| 215 | |||
| 216 | window.WDiffSplitText = function(oldText, newText, text) { | ||
| 217 | |||
| 218 | // convert strange spaces | ||
| 219 | oldText = oldText.replace(/[\t\u000b\u00a0\u2028\u2029]+/g, ' '); | ||
| 220 | newText = newText.replace(/[\t\u000b\u00a0\u2028\u2029]+/g, ' '); | ||
| 221 | |||
| 222 | // split old text into words | ||
| 223 | |||
| 224 | // / | | | | | | | | | | | | | | / | ||
| 225 | var pattern = /[\w]+|\[\[|\]\]|\{\{|\}\}|\n+| +|&\w+;|'''|''|=+|\{\||\|\}|\|\-|./g; | ||
| 226 | var result; | ||
| 227 | do { | ||
| 228 | result = pattern.exec(oldText); | ||
| 229 | if (result != null) { | ||
| 230 | text.oldWords.push(result[0]); | ||
| 231 | } | ||
| 232 | } while (result != null); | ||
| 233 | |||
| 234 | // split new text into words | ||
| 235 | do { | ||
| 236 | result = pattern.exec(newText); | ||
| 237 | if (result != null) { | ||
| 238 | text.newWords.push(result[0]); | ||
| 239 | } | ||
| 240 | } while (result != null); | ||
| 241 | |||
| 242 | return; | ||
| 243 | } | ||
| 244 | |||
| 245 | |||
| 246 | // WDiffText: calculate diff information | ||
| 247 | // input: text.newWords and text.oldWords, arrays containing the texts in arrays of words | ||
| 248 | // optionally for recursive calls: newStart, newEnd, oldStart, oldEnd, recursionLevel | ||
| 249 | // changes: text.newToOld and text.oldToNew, containing the line numbers in the other version | ||
| 250 | |||
| 251 | window.WDiffText = function(text, newStart, newEnd, oldStart, oldEnd, recursionLevel) { | ||
| 252 | |||
| 253 | symbol = new Object(); | ||
| 254 | symbol.newCtr = []; | ||
| 255 | symbol.oldCtr = []; | ||
| 256 | symbol.toNew = []; | ||
| 257 | symbol.toOld = []; | ||
| 258 | |||
| 259 | // set defaults | ||
| 260 | newStart = newStart || 0; | ||
| 261 | newEnd = newEnd || text.newWords.length; | ||
| 262 | oldStart = oldStart || 0; | ||
| 263 | oldEnd = oldEnd || text.oldWords.length; | ||
| 264 | recursionLevel = recursionLevel || 0; | ||
| 265 | |||
| 266 | // limit recursion depth | ||
| 267 | if (recursionLevel > 10) { | ||
| 268 | return; | ||
| 269 | } | ||
| 270 | |||
| 271 | // pass 1: parse new text into symbol table s | ||
| 272 | |||
| 273 | var word; | ||
| 274 | for (var i = newStart; i < newEnd; i ++) { | ||
| 275 | word = text.newWords[i]; | ||
| 276 | |||
| 277 | // add new entry to symbol table | ||
| 278 | if ( symbol[word] == null) { | ||
| 279 | symbol[word] = { newCtr: 0, oldCtr: 0, toNew: null, toOld: null }; | ||
| 280 | } | ||
| 281 | |||
| 282 | // increment symbol table word counter for new text | ||
| 283 | symbol[word].newCtr ++; | ||
| 284 | |||
| 285 | // add last word number in new text | ||
| 286 | symbol[word].toNew = i; | ||
| 287 | } | ||
| 288 | |||
| 289 | // pass 2: parse old text into symbol table | ||
| 290 | |||
| 291 | for (var j = oldStart; j < oldEnd; j ++) { | ||
| 292 | word = text.oldWords[j]; | ||
| 293 | |||
| 294 | // add new entry to symbol table | ||
| 295 | if ( symbol[word] == null) { | ||
| 296 | symbol[word] = { newCtr: 0, oldCtr: 0, toNew: null, toOld: null }; | ||
| 297 | } | ||
| 298 | |||
| 299 | // increment symbol table word counter for old text | ||
| 300 | symbol[word].oldCtr ++; | ||
| 301 | |||
| 302 | // add last word number in old text | ||
| 303 | symbol[word].toOld = j; | ||
| 304 | } | ||
| 305 | |||
| 306 | // pass 3: connect unique words | ||
| 307 | |||
| 308 | for (var i in symbol) { | ||
| 309 | |||
| 310 | // find words in the symbol table that occur only once in both versions | ||
| 311 | if ( (symbol[i].newCtr == 1) && (symbol[i].oldCtr == 1) ) { | ||
| 312 | var toNew = symbol[i].toNew; | ||
| 313 | var toOld = symbol[i].toOld; | ||
| 314 | |||
| 315 | // do not use spaces as unique markers | ||
| 316 | if ( ! /\s/.test( text.newWords[toNew] ) ) { | ||
| 317 | |||
| 318 | // connect from new to old and from old to new | ||
| 319 | text.newToOld[toNew] = toOld; | ||
| 320 | text.oldToNew[toOld] = toNew; | ||
| 321 | } | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | // pass 4: connect adjacent identical words downwards | ||
| 326 | |||
| 327 | for (var i = newStart; i < newEnd - 1; i ++) { | ||
| 328 | |||
| 329 | // find already connected pairs | ||
| 330 | if (text.newToOld[i] != null) { | ||
| 331 | j = text.newToOld[i]; | ||
| 332 | |||
| 333 | // check if the following words are not yet connected | ||
| 334 | if ( (text.newToOld[i + 1] == null) && (text.oldToNew[j + 1] == null) ) { | ||
| 335 | |||
| 336 | // if the following words are the same connect them | ||
| 337 | if ( text.newWords[i + 1] == text.oldWords[j + 1] ) { | ||
| 338 | text.newToOld[i + 1] = j + 1; | ||
| 339 | text.oldToNew[j + 1] = i + 1; | ||
| 340 | } | ||
| 341 | } | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | // pass 5: connect adjacent identical words upwards | ||
| 346 | |||
| 347 | for (var i = newEnd - 1; i > newStart; i --) { | ||
| 348 | |||
| 349 | // find already connected pairs | ||
| 350 | if (text.newToOld[i] != null) { | ||
| 351 | j = text.newToOld[i]; | ||
| 352 | |||
| 353 | // check if the preceeding words are not yet connected | ||
| 354 | if ( (text.newToOld[i - 1] == null) && (text.oldToNew[j - 1] == null) ) { | ||
| 355 | |||
| 356 | // if the preceeding words are the same connect them | ||
| 357 | if ( text.newWords[i - 1] == text.oldWords[j - 1] ) { | ||
| 358 | text.newToOld[i - 1] = j - 1; | ||
| 359 | text.oldToNew[j - 1] = i - 1; | ||
| 360 | } | ||
| 361 | } | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | // recursively diff still unresolved regions downwards | ||
| 366 | |||
| 367 | if (wDiffRecursiveDiff) { | ||
| 368 | i = newStart; | ||
| 369 | j = oldStart; | ||
| 370 | while (i < newEnd) { | ||
| 371 | if (text.newToOld[i - 1] != null) { | ||
| 372 | j = text.newToOld[i - 1] + 1; | ||
| 373 | } | ||
| 374 | |||
| 375 | // check for the start of an unresolved sequence | ||
| 376 | if ( (text.newToOld[i] == null) && (text.oldToNew[j] == null) ) { | ||
| 377 | |||
| 378 | // determine the ends of the sequences | ||
| 379 | var iStart = i; | ||
| 380 | var iEnd = i; | ||
| 381 | while ( (text.newToOld[iEnd] == null) && (iEnd < newEnd) ) { | ||
| 382 | iEnd ++; | ||
| 383 | } | ||
| 384 | var iLength = iEnd - iStart; | ||
| 385 | |||
| 386 | var jStart = j; | ||
| 387 | var jEnd = j; | ||
| 388 | while ( (text.oldToNew[jEnd] == null) && (jEnd < oldEnd) ) { | ||
| 389 | jEnd ++; | ||
| 390 | } | ||
| 391 | var jLength = jEnd - jStart; | ||
| 392 | |||
| 393 | // recursively diff the unresolved sequence | ||
| 394 | if ( (iLength > 0) && (jLength > 0) ) { | ||
| 395 | if ( (iLength > 1) || (jLength > 1) ) { | ||
| 396 | if ( (iStart != newStart) || (iEnd != newEnd) || (jStart != oldStart) || (jEnd != oldEnd) ) { | ||
| 397 | WDiffText(text, iStart, iEnd, jStart, jEnd, recursionLevel + 1); | ||
| 398 | } | ||
| 399 | } | ||
| 400 | } | ||
| 401 | i = iEnd; | ||
| 402 | } | ||
| 403 | else { | ||
| 404 | i ++; | ||
| 405 | } | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | // recursively diff still unresolved regions upwards | ||
| 410 | |||
| 411 | if (wDiffRecursiveDiff) { | ||
| 412 | i = newEnd - 1; | ||
| 413 | j = oldEnd - 1; | ||
| 414 | while (i >= newStart) { | ||
| 415 | if (text.newToOld[i + 1] != null) { | ||
| 416 | j = text.newToOld[i + 1] - 1; | ||
| 417 | } | ||
| 418 | |||
| 419 | // check for the start of an unresolved sequence | ||
| 420 | if ( (text.newToOld[i] == null) && (text.oldToNew[j] == null) ) { | ||
| 421 | |||
| 422 | // determine the ends of the sequences | ||
| 423 | var iStart = i; | ||
| 424 | var iEnd = i + 1; | ||
| 425 | while ( (text.newToOld[iStart - 1] == null) && (iStart >= newStart) ) { | ||
| 426 | iStart --; | ||
| 427 | } | ||
| 428 | var iLength = iEnd - iStart; | ||
| 429 | |||
| 430 | var jStart = j; | ||
| 431 | var jEnd = j + 1; | ||
| 432 | while ( (text.oldToNew[jStart - 1] == null) && (jStart >= oldStart) ) { | ||
| 433 | jStart --; | ||
| 434 | } | ||
| 435 | var jLength = jEnd - jStart; | ||
| 436 | |||
| 437 | // recursively diff the unresolved sequence | ||
| 438 | if ( (iLength > 0) && (jLength > 0) ) { | ||
| 439 | if ( (iLength > 1) || (jLength > 1) ) { | ||
| 440 | if ( (iStart != newStart) || (iEnd != newEnd) || (jStart != oldStart) || (jEnd != oldEnd) ) { | ||
| 441 | WDiffText(text, iStart, iEnd, jStart, jEnd, recursionLevel + 1); | ||
| 442 | } | ||
| 443 | } | ||
| 444 | } | ||
| 445 | i = iStart - 1; | ||
| 446 | } | ||
| 447 | else { | ||
| 448 | i --; | ||
| 449 | } | ||
| 450 | } | ||
| 451 | } | ||
| 452 | return; | ||
| 453 | } | ||
| 454 | |||
| 455 | |||
| 456 | // WDiffToHtml: process diff data into formatted html text | ||
| 457 | // input: text.newWords and text.oldWords, arrays containing the texts in arrays of words | ||
| 458 | // text.newToOld and text.oldToNew, containing the line numbers in the other version | ||
| 459 | // block data structure | ||
| 460 | // returns: outText, a html string | ||
| 461 | |||
| 462 | window.WDiffToHtml = function(text, block) { | ||
| 463 | |||
| 464 | var outText = text.message; | ||
| 465 | |||
| 466 | var blockNumber = 0; | ||
| 467 | var i = 0; | ||
| 468 | var j = 0; | ||
| 469 | var movedAsInsertion; | ||
| 470 | |||
| 471 | // cycle through the new text | ||
| 472 | do { | ||
| 473 | var movedIndex = []; | ||
| 474 | var movedBlock = []; | ||
| 475 | var movedLeft = []; | ||
| 476 | var blockText = ''; | ||
| 477 | var identText = ''; | ||
| 478 | var delText = ''; | ||
| 479 | var insText = ''; | ||
| 480 | var identStart = ''; | ||
| 481 | |||
| 482 | // check if a block ends here and finish previous block | ||
| 483 | if (movedAsInsertion != null) { | ||
| 484 | if (movedAsInsertion == false) { | ||
| 485 | identStart += wDiffHtmlBlockEnd; | ||
| 486 | } | ||
| 487 | else { | ||
| 488 | identStart += wDiffHtmlInsertEnd; | ||
| 489 | } | ||
| 490 | movedAsInsertion = null; | ||
| 491 | } | ||
| 492 | |||
| 493 | // detect block boundary | ||
| 494 | if ( (text.newToOld[i] != j) || (blockNumber == 0 ) ) { | ||
| 495 | if ( ( (text.newToOld[i] != null) || (i >= text.newWords.length) ) && ( (text.oldToNew[j] != null) || (j >= text.oldWords.length) ) ) { | ||
| 496 | |||
| 497 | // block moved right | ||
| 498 | var moved = block.newRight[blockNumber]; | ||
| 499 | if (moved > 0) { | ||
| 500 | var index = block.newRightIndex[blockNumber]; | ||
| 501 | movedIndex.push(index); | ||
| 502 | movedBlock.push(moved); | ||
| 503 | movedLeft.push(false); | ||
| 504 | } | ||
| 505 | |||
| 506 | // block moved left | ||
| 507 | moved = block.newLeft[blockNumber]; | ||
| 508 | if (moved > 0) { | ||
| 509 | var index = block.newLeftIndex[blockNumber]; | ||
| 510 | movedIndex.push(index); | ||
| 511 | movedBlock.push(moved); | ||
| 512 | movedLeft.push(true); | ||
| 513 | } | ||
| 514 | |||
| 515 | // check if a block starts here | ||
| 516 | moved = block.newBlock[blockNumber]; | ||
| 517 | if (moved > 0) { | ||
| 518 | |||
| 519 | // mark block as inserted text | ||
| 520 | if (block.newWords[blockNumber] < wDiffBlockMinLength) { | ||
| 521 | identStart += wDiffHtmlInsertStart; | ||
| 522 | movedAsInsertion = true; | ||
| 523 | } | ||
| 524 | |||
| 525 | // mark block by color | ||
| 526 | else { | ||
| 527 | if (moved > wDiffStyleBlock.length) { | ||
| 528 | moved = wDiffStyleBlock.length; | ||
| 529 | } | ||
| 530 | identStart += WDiffHtmlCustomize(wDiffHtmlBlockStart, moved - 1); | ||
| 531 | movedAsInsertion = false; | ||
| 532 | } | ||
| 533 | } | ||
| 534 | |||
| 535 | if (i >= text.newWords.length) { | ||
| 536 | i ++; | ||
| 537 | } | ||
| 538 | else { | ||
| 539 | j = text.newToOld[i]; | ||
| 540 | blockNumber ++; | ||
| 541 | } | ||
| 542 | } | ||
| 543 | } | ||
| 544 | |||
| 545 | // get the correct order if moved to the left as well as to the right from here | ||
| 546 | if (movedIndex.length == 2) { | ||
| 547 | if (movedIndex[0] > movedIndex[1]) { | ||
| 548 | movedIndex.reverse(); | ||
| 549 | movedBlock.reverse(); | ||
| 550 | movedLeft.reverse(); | ||
| 551 | } | ||
| 552 | } | ||
| 553 | |||
| 554 | // handle left and right block moves from this position | ||
| 555 | for (var m = 0; m < movedIndex.length; m ++) { | ||
| 556 | |||
| 557 | // insert the block as deleted text | ||
| 558 | if (block.newWords[ movedIndex[m] ] < wDiffBlockMinLength) { | ||
| 559 | var movedStart = block.newStart[ movedIndex[m] ]; | ||
| 560 | var movedLength = block.newLength[ movedIndex[m] ]; | ||
| 561 | var str = ''; | ||
| 562 | for (var n = movedStart; n < movedStart + movedLength; n ++) { | ||
| 563 | str += text.newWords[n]; | ||
| 564 | } | ||
| 565 | str = WDiffEscape(str); | ||
| 566 | str = str.replace(/\n/g, '¶<br>'); | ||
| 567 | blockText += wDiffHtmlDeleteStart + str + wDiffHtmlDeleteEnd; | ||
| 568 | } | ||
| 569 | |||
| 570 | // add a placeholder / move direction indicator | ||
| 571 | else { | ||
| 572 | if (movedBlock[m] > wDiffStyleBlock.length) { | ||
| 573 | movedBlock[m] = wDiffStyleBlock.length; | ||
| 574 | } | ||
| 575 | if (movedLeft[m]) { | ||
| 576 | blockText += WDiffHtmlCustomize(wDiffHtmlMovedLeft, movedBlock[m] - 1); | ||
| 577 | } | ||
| 578 | else { | ||
| 579 | blockText += WDiffHtmlCustomize(wDiffHtmlMovedRight, movedBlock[m] - 1); | ||
| 580 | } | ||
| 581 | } | ||
| 582 | } | ||
| 583 | |||
| 584 | // collect consecutive identical text | ||
| 585 | while ( (i < text.newWords.length) && (j < text.oldWords.length) ) { | ||
| 586 | if ( (text.newToOld[i] == null) || (text.oldToNew[j] == null) ) { | ||
| 587 | break; | ||
| 588 | } | ||
| 589 | if (text.newToOld[i] != j) { | ||
| 590 | break; | ||
| 591 | } | ||
| 592 | identText += text.newWords[i]; | ||
| 593 | i ++; | ||
| 594 | j ++; | ||
| 595 | } | ||
| 596 | |||
| 597 | // collect consecutive deletions | ||
| 598 | while ( (text.oldToNew[j] == null) && (j < text.oldWords.length) ) { | ||
| 599 | delText += text.oldWords[j]; | ||
| 600 | j ++; | ||
| 601 | } | ||
| 602 | |||
| 603 | // collect consecutive inserts | ||
| 604 | while ( (text.newToOld[i] == null) && (i < text.newWords.length) ) { | ||
| 605 | insText += text.newWords[i]; | ||
| 606 | i ++; | ||
| 607 | } | ||
| 608 | |||
| 609 | // remove leading and trailing similarities betweein delText and ins from highlighting | ||
| 610 | var preText = ''; | ||
| 611 | var postText = ''; | ||
| 612 | if (wDiffWordDiff) { | ||
| 613 | if ( (delText != '') && (insText != '') ) { | ||
| 614 | |||
| 615 | // remove leading similarities | ||
| 616 | while ( delText.charAt(0) == insText.charAt(0) && (delText != '') && (insText != '') ) { | ||
| 617 | preText = preText + delText.charAt(0); | ||
| 618 | delText = delText.substr(1); | ||
| 619 | insText = insText.substr(1); | ||
| 620 | } | ||
| 621 | |||
| 622 | // remove trailing similarities | ||
| 623 | while ( delText.charAt(delText.length - 1) == insText.charAt(insText.length - 1) && (delText != '') && (insText != '') ) { | ||
| 624 | postText = delText.charAt(delText.length - 1) + postText; | ||
| 625 | delText = delText.substr(0, delText.length - 1); | ||
| 626 | insText = insText.substr(0, insText.length - 1); | ||
| 627 | } | ||
| 628 | } | ||
| 629 | } | ||
| 630 | |||
| 631 | // output the identical text, deletions and inserts | ||
| 632 | |||
| 633 | // moved from here indicator | ||
| 634 | if (blockText != '') { | ||
| 635 | outText += blockText; | ||
| 636 | } | ||
| 637 | |||
| 638 | // identical text | ||
| 639 | if (identText != '') { | ||
| 640 | outText += identStart + WDiffEscape(identText); | ||
| 641 | } | ||
| 642 | outText += preText; | ||
| 643 | |||
| 644 | // deleted text | ||
| 645 | if (delText != '') { | ||
| 646 | delText = wDiffHtmlDeleteStart + WDiffEscape(delText) + wDiffHtmlDeleteEnd; | ||
| 647 | delText = delText.replace(/\n/g, '¶<br>'); | ||
| 648 | outText += delText; | ||
| 649 | } | ||
| 650 | |||
| 651 | // inserted text | ||
| 652 | if (insText != '') { | ||
| 653 | insText = wDiffHtmlInsertStart + WDiffEscape(insText) + wDiffHtmlInsertEnd; | ||
| 654 | insText = insText.replace(/\n/g, '¶<br>'); | ||
| 655 | outText += insText; | ||
| 656 | } | ||
| 657 | outText += postText; | ||
| 658 | } while (i <= text.newWords.length); | ||
| 659 | |||
| 660 | outText += '\n'; | ||
| 661 | outText = WDiffHtmlFormat(outText); | ||
| 662 | |||
| 663 | return(outText); | ||
| 664 | } | ||
| 665 | |||
| 666 | |||
| 667 | // WDiffEscape: replaces html-sensitive characters in output text with character entities | ||
| 668 | |||
| 669 | window.WDiffEscape = function(text) { | ||
| 670 | |||
| 671 | text = text.replace(/&/g, '&'); | ||
| 672 | text = text.replace(/</g, '<'); | ||
| 673 | text = text.replace(/>/g, '>'); | ||
| 674 | text = text.replace(/\"/g, '"'); | ||
| 675 | |||
| 676 | return(text); | ||
| 677 | } | ||
| 678 | |||
| 679 | |||
| 680 | // HtmlCustomize: customize indicator html: replace {number} with the block number, {block} with the block style | ||
| 681 | |||
| 682 | window.WDiffHtmlCustomize = function(text, block) { | ||
| 683 | |||
| 684 | text = text.replace(/\{number\}/, block); | ||
| 685 | text = text.replace(/\{block\}/, wDiffStyleBlock[block]); | ||
| 686 | |||
| 687 | return(text); | ||
| 688 | } | ||
| 689 | |||
| 690 | |||
| 691 | // HtmlFormat: replaces newlines and multiple spaces in text with html code | ||
| 692 | |||
| 693 | window.WDiffHtmlFormat = function(text) { | ||
| 694 | |||
| 695 | text = text.replace(/ /g, ' '); | ||
| 696 | text = text.replace(/\n/g, '<br>'); | ||
| 697 | |||
| 698 | return(text); | ||
| 699 | } | ||
| 700 | |||
| 701 | |||
| 702 | // WDiffDetectBlocks: detect block borders and moved blocks | ||
| 703 | // input: text object, block object | ||
| 704 | |||
| 705 | window.WDiffDetectBlocks = function(text, block) { | ||
| 706 | |||
| 707 | block.oldStart = []; | ||
| 708 | block.oldToNew = []; | ||
| 709 | block.oldLength = []; | ||
| 710 | block.oldWords = []; | ||
| 711 | block.newStart = []; | ||
| 712 | block.newLength = []; | ||
| 713 | block.newWords = []; | ||
| 714 | block.newNumber = []; | ||
| 715 | block.newBlock = []; | ||
| 716 | block.newLeft = []; | ||
| 717 | block.newRight = []; | ||
| 718 | block.newLeftIndex = []; | ||
| 719 | block.newRightIndex = []; | ||
| 720 | |||
| 721 | var blockNumber = 0; | ||
| 722 | var wordCounter = 0; | ||
| 723 | var realWordCounter = 0; | ||
| 724 | |||
| 725 | // get old text block order | ||
| 726 | if (wDiffShowBlockMoves) { | ||
| 727 | var j = 0; | ||
| 728 | var i = 0; | ||
| 729 | do { | ||
| 730 | |||
| 731 | // detect block boundaries on old text | ||
| 732 | if ( (text.oldToNew[j] != i) || (blockNumber == 0 ) ) { | ||
| 733 | if ( ( (text.oldToNew[j] != null) || (j >= text.oldWords.length) ) && ( (text.newToOld[i] != null) || (i >= text.newWords.length) ) ) { | ||
| 734 | if (blockNumber > 0) { | ||
| 735 | block.oldLength[blockNumber - 1] = wordCounter; | ||
| 736 | block.oldWords[blockNumber - 1] = realWordCounter; | ||
| 737 | wordCounter = 0; | ||
| 738 | realWordCounter = 0; | ||
| 739 | } | ||
| 740 | |||
| 741 | if (j >= text.oldWords.length) { | ||
| 742 | j ++; | ||
| 743 | } | ||
| 744 | else { | ||
| 745 | i = text.oldToNew[j]; | ||
| 746 | block.oldStart[blockNumber] = j; | ||
| 747 | block.oldToNew[blockNumber] = text.oldToNew[j]; | ||
| 748 | blockNumber ++; | ||
| 749 | } | ||
| 750 | } | ||
| 751 | } | ||
| 752 | |||
| 753 | // jump over identical pairs | ||
| 754 | while ( (i < text.newWords.length) && (j < text.oldWords.length) ) { | ||
| 755 | if ( (text.newToOld[i] == null) || (text.oldToNew[j] == null) ) { | ||
| 756 | break; | ||
| 757 | } | ||
| 758 | if (text.oldToNew[j] != i) { | ||
| 759 | break; | ||
| 760 | } | ||
| 761 | i ++; | ||
| 762 | j ++; | ||
| 763 | wordCounter ++; | ||
| 764 | if ( /\w/.test( text.newWords[i] ) ) { | ||
| 765 | realWordCounter ++; | ||
| 766 | } | ||
| 767 | } | ||
| 768 | |||
| 769 | // jump over consecutive deletions | ||
| 770 | while ( (text.oldToNew[j] == null) && (j < text.oldWords.length) ) { | ||
| 771 | j ++; | ||
| 772 | } | ||
| 773 | |||
| 774 | // jump over consecutive inserts | ||
| 775 | while ( (text.newToOld[i] == null) && (i < text.newWords.length) ) { | ||
| 776 | i ++; | ||
| 777 | } | ||
| 778 | } while (j <= text.oldWords.length); | ||
| 779 | |||
| 780 | // get the block order in the new text | ||
| 781 | var lastMin; | ||
| 782 | var currMinIndex; | ||
| 783 | lastMin = null; | ||
| 784 | |||
| 785 | // sort the data by increasing start numbers into new text block info | ||
| 786 | for (var i = 0; i < blockNumber; i ++) { | ||
| 787 | currMin = null; | ||
| 788 | for (var j = 0; j < blockNumber; j ++) { | ||
| 789 | curr = block.oldToNew[j]; | ||
| 790 | if ( (curr > lastMin) || (lastMin == null) ) { | ||
| 791 | if ( (curr < currMin) || (currMin == null) ) { | ||
| 792 | currMin = curr; | ||
| 793 | currMinIndex = j; | ||
| 794 | } | ||
| 795 | } | ||
| 796 | } | ||
| 797 | block.newStart[i] = block.oldToNew[currMinIndex]; | ||
| 798 | block.newLength[i] = block.oldLength[currMinIndex]; | ||
| 799 | block.newWords[i] = block.oldWords[currMinIndex]; | ||
| 800 | block.newNumber[i] = currMinIndex; | ||
| 801 | lastMin = currMin; | ||
| 802 | } | ||
| 803 | |||
| 804 | // detect not moved blocks | ||
| 805 | for (var i = 0; i < blockNumber; i ++) { | ||
| 806 | if (block.newBlock[i] == null) { | ||
| 807 | if (block.newNumber[i] == i) { | ||
| 808 | block.newBlock[i] = 0; | ||
| 809 | } | ||
| 810 | } | ||
| 811 | } | ||
| 812 | |||
| 813 | // detect switches of neighbouring blocks | ||
| 814 | for (var i = 0; i < blockNumber - 1; i ++) { | ||
| 815 | if ( (block.newBlock[i] == null) && (block.newBlock[i + 1] == null) ) { | ||
| 816 | if (block.newNumber[i] - block.newNumber[i + 1] == 1) { | ||
| 817 | if ( (block.newNumber[i + 1] - block.newNumber[i + 2] != 1) || (i + 2 >= blockNumber) ) { | ||
| 818 | |||
| 819 | // the shorter one is declared the moved one | ||
| 820 | if (block.newLength[i] < block.newLength[i + 1]) { | ||
| 821 | block.newBlock[i] = 1; | ||
| 822 | block.newBlock[i + 1] = 0; | ||
| 823 | } | ||
| 824 | else { | ||
| 825 | block.newBlock[i] = 0; | ||
| 826 | block.newBlock[i + 1] = 1; | ||
| 827 | } | ||
| 828 | } | ||
| 829 | } | ||
| 830 | } | ||
| 831 | } | ||
| 832 | |||
| 833 | // mark all others as moved and number the moved blocks | ||
| 834 | j = 1; | ||
| 835 | for (var i = 0; i < blockNumber; i ++) { | ||
| 836 | if ( (block.newBlock[i] == null) || (block.newBlock[i] == 1) ) { | ||
| 837 | block.newBlock[i] = j++; | ||
| 838 | } | ||
| 839 | } | ||
| 840 | |||
| 841 | // check if a block has been moved from this block border | ||
| 842 | for (var i = 0; i < blockNumber; i ++) { | ||
| 843 | for (var j = 0; j < blockNumber; j ++) { | ||
| 844 | |||
| 845 | if (block.newNumber[j] == i) { | ||
| 846 | if (block.newBlock[j] > 0) { | ||
| 847 | |||
| 848 | // block moved right | ||
| 849 | if (block.newNumber[j] < j) { | ||
| 850 | block.newRight[i] = block.newBlock[j]; | ||
| 851 | block.newRightIndex[i] = j; | ||
| 852 | } | ||
| 853 | |||
| 854 | // block moved left | ||
| 855 | else { | ||
| 856 | block.newLeft[i + 1] = block.newBlock[j]; | ||
| 857 | block.newLeftIndex[i + 1] = j; | ||
| 858 | } | ||
| 859 | } | ||
| 860 | } | ||
| 861 | } | ||
| 862 | } | ||
| 863 | } | ||
| 864 | return; | ||
| 865 | } | ||
| 866 | |||
| 867 | |||
| 868 | // WDiffShortenOutput: remove unchanged parts from final output | ||
| 869 | // input: the output of WDiffString | ||
| 870 | // returns: the text with removed unchanged passages indicated by (...) | ||
| 871 | |||
| 872 | window.WDiffShortenOutput = function(diffText) { | ||
| 873 | |||
| 874 | // html <br/> to newlines | ||
| 875 | diffText = diffText.replace(/<br[^>]*>/g, '\n'); | ||
| 876 | |||
| 877 | // scan for diff html tags | ||
| 878 | var regExpDiff = new RegExp('<\\w+ class=\\"(\\w+)\\"[^>]*>(.|\\n)*?<!--\\1-->', 'g'); | ||
| 879 | var tagStart = []; | ||
| 880 | var tagEnd = []; | ||
| 881 | var i = 0; | ||
| 882 | var found; | ||
| 883 | while ( (found = regExpDiff.exec(diffText)) != null ) { | ||
| 884 | |||
| 885 | // combine consecutive diff tags | ||
| 886 | if ( (i > 0) && (tagEnd[i - 1] == found.index) ) { | ||
| 887 | tagEnd[i - 1] = found.index + found[0].length; | ||
| 888 | } | ||
| 889 | else { | ||
| 890 | tagStart[i] = found.index; | ||
| 891 | tagEnd[i] = found.index + found[0].length; | ||
| 892 | i ++; | ||
| 893 | } | ||
| 894 | } | ||
| 895 | |||
| 896 | // no diff tags detected | ||
| 897 | if (tagStart.length == 0) { | ||
| 898 | return(wDiffNoChange); | ||
| 899 | } | ||
| 900 | |||
| 901 | // define regexps | ||
| 902 | var regExpHeading = new RegExp('\\n=+.+?=+ *\\n|\\n\\{\\||\\n\\|\\}', 'g'); | ||
| 903 | var regExpParagraph = new RegExp('\\n\\n+', 'g'); | ||
| 904 | var regExpLine = new RegExp('\\n+', 'g'); | ||
| 905 | var regExpBlank = new RegExp('(<[^>]+>)*\\s+', 'g'); | ||
| 906 | |||
| 907 | // determine fragment border positions around diff tags | ||
| 908 | var rangeStart = []; | ||
| 909 | var rangeEnd = []; | ||
| 910 | var rangeStartType = []; | ||
| 911 | var rangeEndType = []; | ||
| 912 | for (var i = 0; i < tagStart.length; i ++) { | ||
| 913 | var found; | ||
| 914 | |||
| 915 | // find last heading before diff tag | ||
| 916 | var lastPos = tagStart[i] - wDiffHeadingBefore; | ||
| 917 | if (lastPos < 0) { | ||
| 918 | lastPos = 0; | ||
| 919 | } | ||
| 920 | regExpHeading.lastIndex = lastPos; | ||
| 921 | while ( (found = regExpHeading.exec(diffText)) != null ) { | ||
| 922 | if (found.index > tagStart[i]) { | ||
| 923 | break; | ||
| 924 | } | ||
| 925 | rangeStart[i] = found.index; | ||
| 926 | rangeStartType[i] = 'heading'; | ||
| 927 | } | ||
| 928 | |||
| 929 | // find last paragraph before diff tag | ||
| 930 | if (rangeStart[i] == null) { | ||
| 931 | lastPos = tagStart[i] - wDiffParagraphBefore; | ||
| 932 | if (lastPos < 0) { | ||
| 933 | lastPos = 0; | ||
| 934 | } | ||
| 935 | regExpParagraph.lastIndex = lastPos; | ||
| 936 | while ( (found = regExpParagraph.exec(diffText)) != null ) { | ||
| 937 | if (found.index > tagStart[i]) { | ||
| 938 | break; | ||
| 939 | } | ||
| 940 | rangeStart[i] = found.index; | ||
| 941 | rangeStartType[i] = 'paragraph'; | ||
| 942 | } | ||
| 943 | } | ||
| 944 | |||
| 945 | // find line break before diff tag | ||
| 946 | if (rangeStart[i] == null) { | ||
| 947 | lastPos = tagStart[i] - wDiffLineBeforeMax; | ||
| 948 | if (lastPos < 0) { | ||
| 949 | lastPos = 0; | ||
| 950 | } | ||
| 951 | regExpLine.lastIndex = lastPos; | ||
| 952 | while ( (found = regExpLine.exec(diffText)) != null ) { | ||
| 953 | if (found.index > tagStart[i] - wDiffLineBeforeMin) { | ||
| 954 | break; | ||
| 955 | } | ||
| 956 | rangeStart[i] = found.index; | ||
| 957 | rangeStartType[i] = 'line'; | ||
| 958 | } | ||
| 959 | } | ||
| 960 | |||
| 961 | // find blank before diff tag | ||
| 962 | if (rangeStart[i] == null) { | ||
| 963 | lastPos = tagStart[i] - wDiffBlankBeforeMax; | ||
| 964 | if (lastPos < 0) { | ||
| 965 | lastPos = 0; | ||
| 966 | } | ||
| 967 | regExpBlank.lastIndex = lastPos; | ||
| 968 | while ( (found = regExpBlank.exec(diffText)) != null ) { | ||
| 969 | if (found.index > tagStart[i] - wDiffBlankBeforeMin) { | ||
| 970 | break; | ||
| 971 | } | ||
| 972 | rangeStart[i] = found.index; | ||
| 973 | rangeStartType[i] = 'blank'; | ||
| 974 | } | ||
| 975 | } | ||
| 976 | |||
| 977 | // fixed number of chars before diff tag | ||
| 978 | if (rangeStart[i] == null) { | ||
| 979 | rangeStart[i] = tagStart[i] - wDiffCharsBefore; | ||
| 980 | rangeStartType[i] = 'chars'; | ||
| 981 | if (rangeStart[i] < 0) { | ||
| 982 | rangeStart[i] = 0; | ||
| 983 | } | ||
| 984 | } | ||
| 985 | |||
| 986 | // find first heading after diff tag | ||
| 987 | regExpHeading.lastIndex = tagEnd[i]; | ||
| 988 | if ( (found = regExpHeading.exec(diffText)) != null ) { | ||
| 989 | if (found.index < tagEnd[i] + wDiffHeadingAfter) { | ||
| 990 | rangeEnd[i] = found.index + found[0].length; | ||
| 991 | rangeEndType[i] = 'heading'; | ||
| 992 | } | ||
| 993 | } | ||
| 994 | |||
| 995 | // find first paragraph after diff tag | ||
| 996 | if (rangeEnd[i] == null) { | ||
| 997 | regExpParagraph.lastIndex = tagEnd[i]; | ||
| 998 | if ( (found = regExpParagraph.exec(diffText)) != null ) { | ||
| 999 | if (found.index < tagEnd[i] + wDiffParagraphAfter) { | ||
| 1000 | rangeEnd[i] = found.index; | ||
| 1001 | rangeEndType[i] = 'paragraph'; | ||
| 1002 | } | ||
| 1003 | } | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | // find first line break after diff tag | ||
| 1007 | if (rangeEnd[i] == null) { | ||
| 1008 | regExpLine.lastIndex = tagEnd[i] + wDiffLineAfterMin; | ||
| 1009 | if ( (found = regExpLine.exec(diffText)) != null ) { | ||
| 1010 | if (found.index < tagEnd[i] + wDiffLineAfterMax) { | ||
| 1011 | rangeEnd[i] = found.index; | ||
| 1012 | rangeEndType[i] = 'break'; | ||
| 1013 | } | ||
| 1014 | } | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | // find blank after diff tag | ||
| 1018 | if (rangeEnd[i] == null) { | ||
| 1019 | regExpBlank.lastIndex = tagEnd[i] + wDiffBlankAfterMin; | ||
| 1020 | if ( (found = regExpBlank.exec(diffText)) != null ) { | ||
| 1021 | if (found.index < tagEnd[i] + wDiffBlankAfterMax) { | ||
| 1022 | rangeEnd[i] = found.index; | ||
| 1023 | rangeEndType[i] = 'blank'; | ||
| 1024 | } | ||
| 1025 | } | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | // fixed number of chars after diff tag | ||
| 1029 | if (rangeEnd[i] == null) { | ||
| 1030 | rangeEnd[i] = tagEnd[i] + wDiffCharsAfter; | ||
| 1031 | if (rangeEnd[i] > diffText.length) { | ||
| 1032 | rangeEnd[i] = diffText.length; | ||
| 1033 | rangeEndType[i] = 'chars'; | ||
| 1034 | } | ||
| 1035 | } | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | // remove overlaps, join close fragments | ||
| 1039 | var fragmentStart = []; | ||
| 1040 | var fragmentEnd = []; | ||
| 1041 | var fragmentStartType = []; | ||
| 1042 | var fragmentEndType = []; | ||
| 1043 | fragmentStart[0] = rangeStart[0]; | ||
| 1044 | fragmentEnd[0] = rangeEnd[0]; | ||
| 1045 | fragmentStartType[0] = rangeStartType[0]; | ||
| 1046 | fragmentEndType[0] = rangeEndType[0]; | ||
| 1047 | var j = 1; | ||
| 1048 | for (var i = 1; i < rangeStart.length; i ++) { | ||
| 1049 | if (rangeStart[i] > fragmentEnd[j - 1] + wDiffFragmentJoin) { | ||
| 1050 | fragmentStart[j] = rangeStart[i]; | ||
| 1051 | fragmentEnd[j] = rangeEnd[i]; | ||
| 1052 | fragmentStartType[j] = rangeStartType[i]; | ||
| 1053 | fragmentEndType[j] = rangeEndType[i]; | ||
| 1054 | j ++; | ||
| 1055 | } | ||
| 1056 | else { | ||
| 1057 | fragmentEnd[j - 1] = rangeEnd[i]; | ||
| 1058 | fragmentEndType[j - 1] = rangeEndType[i]; | ||
| 1059 | } | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | // assemble the fragments | ||
| 1063 | var outText = ''; | ||
| 1064 | for (var i = 0; i < fragmentStart.length; i ++) { | ||
| 1065 | |||
| 1066 | // get text fragment | ||
| 1067 | var fragment = diffText.substring(fragmentStart[i], fragmentEnd[i]); | ||
| 1068 | var fragment = fragment.replace(/^\n+|\n+$/g, ''); | ||
| 1069 | |||
| 1070 | // add inline marks for omitted chars and words | ||
| 1071 | if (fragmentStart[i] > 0) { | ||
| 1072 | if (fragmentStartType[i] == 'chars') { | ||
| 1073 | fragment = wDiffOmittedChars + fragment; | ||
| 1074 | } | ||
| 1075 | else if (fragmentStartType[i] == 'blank') { | ||
| 1076 | fragment = wDiffOmittedChars + ' ' + fragment; | ||
| 1077 | } | ||
| 1078 | } | ||
| 1079 | if (fragmentEnd[i] < diffText.length) { | ||
| 1080 | if (fragmentStartType[i] == 'chars') { | ||
| 1081 | fragment = fragment + wDiffOmittedChars; | ||
| 1082 | } | ||
| 1083 | else if (fragmentStartType[i] == 'blank') { | ||
| 1084 | fragment = fragment + ' ' + wDiffOmittedChars; | ||
| 1085 | } | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | // add omitted line separator | ||
| 1089 | if (fragmentStart[i] > 0) { | ||
| 1090 | outText += wDiffOmittedLines; | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | // encapsulate span errors | ||
| 1094 | outText += '<div>' + fragment + '</div>'; | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | // add trailing omitted line separator | ||
| 1098 | if (fragmentEnd[i - 1] < diffText.length) { | ||
| 1099 | outText = outText + wDiffOmittedLines; | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | // remove leading and trailing empty lines | ||
| 1103 | outText = outText.replace(/^(<div>)\n+|\n+(<\/div>)$/g, '$1$2'); | ||
| 1104 | |||
| 1105 | // convert to html linebreaks | ||
| 1106 | outText = outText.replace(/\n/g, '<br />'); | ||
| 1107 | |||
| 1108 | return(outText); | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | |||
| 1112 | // <pre><nowiki> \ No newline at end of file | ||
diff --git a/public/javascripts/related_assets.js b/public/javascripts/related_assets.js new file mode 100644 index 0000000..2955cbf --- /dev/null +++ b/public/javascripts/related_assets.js | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | related_assets = { | ||
| 2 | initialize: function() { | ||
| 3 | var container = $("#related_assets"); | ||
| 4 | var searchUrl = container.data("search-url"); | ||
| 5 | var createUrl = container.data("create-url"); | ||
| 6 | var list = $("#related_asset_list"); | ||
| 7 | |||
| 8 | initSearchPicker({ | ||
| 9 | inputSelector: "#related_asset_search_term", | ||
| 10 | resultsSelector: "#related_asset_search_results", | ||
| 11 | url: searchUrl, | ||
| 12 | loadOnFocus: true, | ||
| 13 | renderResults: function(data, results) { | ||
| 14 | var found = false; | ||
| 15 | data.forEach(function(asset) { | ||
| 16 | var item = $( | ||
| 17 | "<a href='#' class='related_asset_result'>" + | ||
| 18 | "<img src='" + asset.thumb_url + "' alt=''>" + | ||
| 19 | "<span>" + asset.name + "</span>" + | ||
| 20 | "</a>" | ||
| 21 | ); | ||
| 22 | item.bind("click", function() { | ||
| 23 | related_assets.attach(asset.id, createUrl, list); | ||
| 24 | return false; | ||
| 25 | }); | ||
| 26 | results.append(item); | ||
| 27 | found = true; | ||
| 28 | }); | ||
| 29 | return found; | ||
| 30 | } | ||
| 31 | }); | ||
| 32 | |||
| 33 | $(document).on("click", function(e) { | ||
| 34 | if (!$(e.target).closest("#related_assets").length) { | ||
| 35 | $("#related_asset_search_results").slideUp().empty(); | ||
| 36 | } | ||
| 37 | }); | ||
| 38 | |||
| 39 | list.sortable({ | ||
| 40 | handle: ".related_asset_handle", | ||
| 41 | update: function(event, ui) { | ||
| 42 | var newPosition = ui.item.index() + 1; // acts_as_list is 1-based; jQuery UI's index() is 0-based | ||
| 43 | $.ajax({ | ||
| 44 | type: "PATCH", | ||
| 45 | url: ui.item.data("url"), | ||
| 46 | data: "position=" + newPosition | ||
| 47 | }); | ||
| 48 | } | ||
| 49 | }); | ||
| 50 | |||
| 51 | list.on("click", ".related_asset_remove", function() { | ||
| 52 | var item = $(this).closest("li"); | ||
| 53 | $.ajax({ | ||
| 54 | type: "DELETE", | ||
| 55 | url: item.data("url"), | ||
| 56 | success: function() { | ||
| 57 | item.remove(); | ||
| 58 | } | ||
| 59 | }); | ||
| 60 | }); | ||
| 61 | }, | ||
| 62 | |||
| 63 | attach: function(assetId, createUrl, list) { | ||
| 64 | $.ajax({ | ||
| 65 | type: "POST", | ||
| 66 | url: createUrl, | ||
| 67 | data: "asset_id=" + assetId, | ||
| 68 | dataType: "json", | ||
| 69 | success: function(related) { | ||
| 70 | var item = $($("#related_asset_template").html().trim()); | ||
| 71 | item.attr("data-url", related.url); | ||
| 72 | item.find("img").attr("src", related.thumb_url); | ||
| 73 | list.append(item); | ||
| 74 | list.sortable("refresh"); | ||
| 75 | $("#related_asset_search_term").val(""); | ||
| 76 | $("#related_asset_search_results").slideUp().empty(); | ||
| 77 | } | ||
| 78 | }); | ||
| 79 | } | ||
| 80 | }; | ||
diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css index c9ef173..f1f4c05 100644 --- a/public/stylesheets/admin.css +++ b/public/stylesheets/admin.css | |||
| @@ -1,4 +1,6 @@ | |||
| 1 | /* Layout */ | 1 | /* ============================================================ |
| 2 | Base elements | ||
| 3 | ============================================================ */ | ||
| 2 | 4 | ||
| 3 | body { | 5 | body { |
| 4 | font-family: Helvetica, Arial, sans-serif; | 6 | font-family: Helvetica, Arial, sans-serif; |
| @@ -6,9 +8,16 @@ body { | |||
| 6 | margin: 0px; | 8 | margin: 0px; |
| 7 | } | 9 | } |
| 8 | 10 | ||
| 11 | /* Plain link visibility: a light, desaturated squiggly underline | ||
| 12 | signals "clickable" at rest without the weight of a solid line. */ | ||
| 9 | a { | 13 | a { |
| 10 | color: #000000; | 14 | color: #000000; |
| 11 | text-decoration: none; | 15 | text-decoration: underline; |
| 16 | -webkit-text-decoration-style: wavy; | ||
| 17 | text-decoration-style: wavy; | ||
| 18 | text-decoration-color: #b0b0b0; | ||
| 19 | text-decoration-thickness: 1px; | ||
| 20 | text-underline-offset: 2px; | ||
| 12 | } | 21 | } |
| 13 | 22 | ||
| 14 | a:hover { | 23 | a:hover { |
| @@ -19,29 +28,117 @@ th { | |||
| 19 | text-transform: lowercase; | 28 | text-transform: lowercase; |
| 20 | } | 29 | } |
| 21 | 30 | ||
| 31 | td { | ||
| 32 | padding-top: 10px; | ||
| 33 | padding-bottom: 5px; | ||
| 34 | padding-right: 10px; | ||
| 35 | } | ||
| 36 | /* table#revisions td only overrides padding-right, so its rows still | ||
| 37 | depend on this rule for vertical padding. table.node_table td resets | ||
| 38 | padding-top/bottom to 0 explicitly and does not depend on it. */ | ||
| 39 | |||
| 40 | input[type=text], textarea { | ||
| 41 | font-size: 1rem; | ||
| 42 | font-family: Helvetica; | ||
| 43 | border: 1px solid #989898; | ||
| 44 | } | ||
| 45 | |||
| 46 | select { | ||
| 47 | font-size: 1rem; | ||
| 48 | border: 1px solid #989898; | ||
| 49 | } | ||
| 50 | |||
| 51 | summary { | ||
| 52 | cursor: pointer; | ||
| 53 | } | ||
| 54 | |||
| 55 | summary:hover { | ||
| 56 | background-color: #f7f7f7; | ||
| 57 | } | ||
| 58 | |||
| 59 | input[type=password] { | ||
| 60 | border: 1px solid #989898; | ||
| 61 | } | ||
| 62 | |||
| 63 | input[type=radio] { | ||
| 64 | border: 1px solid #989898; | ||
| 65 | } | ||
| 66 | |||
| 67 | /* ============================================================ | ||
| 68 | Layout | ||
| 69 | ============================================================ */ | ||
| 70 | |||
| 71 | @media(min-width:1016px) { | ||
| 72 | #wrapper { | ||
| 73 | margin: 0 125px; | ||
| 74 | } | ||
| 75 | .node_action_bar.node_action_bar_save { | ||
| 76 | margin-left: 120px; | ||
| 77 | } | ||
| 78 | |||
| 79 | .standalone_action_bar { | ||
| 80 | margin-left: -125px; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | @media(max-width:1015px) { | ||
| 84 | #wrapper { | ||
| 85 | margin: 0; | ||
| 86 | width: 100%; | ||
| 87 | box-sizing: border-box; | ||
| 88 | } | ||
| 89 | |||
| 90 | #sitemap details details { | ||
| 91 | margin-left: 0.75rem; | ||
| 92 | padding-left: 0.5rem; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 22 | #metadata, | 96 | #metadata, |
| 23 | #content { | 97 | #content { |
| 24 | margin-left: 5px; | 98 | margin-left: 5px; |
| 25 | } | 99 | } |
| 26 | 100 | ||
| 27 | @media(min-width:1016px) { | 101 | #metadata_details summary { |
| 28 | #wrapper { | 102 | margin-bottom: 1em; |
| 29 | margin: 0 125px; | ||
| 30 | } | 103 | } |
| 104 | |||
| 105 | .right { text-align: right; } | ||
| 106 | .clear_left { clear: left; } | ||
| 107 | |||
| 108 | /* ============================================================ | ||
| 109 | Navigation | ||
| 110 | ============================================================ */ | ||
| 111 | |||
| 112 | #navigation { | ||
| 113 | position: relative; | ||
| 114 | margin-top: 10px; | ||
| 31 | } | 115 | } |
| 32 | @media(max-width:1015px) { | 116 | |
| 33 | #wrapper { | 117 | .admin_content_spacer { |
| 34 | margin: 0; | 118 | clear: both; |
| 35 | width: 100%; | 119 | margin-bottom: 40px; |
| 36 | box-sizing: border-box; | ||
| 37 | } | 120 | } |
| 121 | |||
| 122 | .node_action_bar { | ||
| 123 | display: flex; | ||
| 124 | flex-wrap: wrap; | ||
| 125 | align-items: center; | ||
| 126 | gap: 0.75rem; | ||
| 127 | margin: 1rem 0 1.5rem; | ||
| 128 | padding-bottom: 1rem; | ||
| 129 | border-bottom: 1px solid #e8e8e8; | ||
| 38 | } | 130 | } |
| 39 | 131 | ||
| 132 | /* ============================================================ | ||
| 133 | Flash / notices | ||
| 134 | ============================================================ */ | ||
| 40 | 135 | ||
| 41 | #flash { | 136 | #flash { |
| 42 | height: 12px; | 137 | height: auto; |
| 43 | line-height: 20px; | 138 | line-height: 20px; |
| 44 | margin-top: 40px; | 139 | padding: 6px 10px; |
| 140 | border-left: 3px solid #969696; | ||
| 141 | background-color: #f1f1f1; | ||
| 45 | margin-bottom: 20px; | 142 | margin-bottom: 20px; |
| 46 | margin-left: 5px; | 143 | margin-left: 5px; |
| 47 | } | 144 | } |
| @@ -52,7 +149,6 @@ th { | |||
| 52 | text-transform: lowercase; | 149 | text-transform: lowercase; |
| 53 | } | 150 | } |
| 54 | 151 | ||
| 55 | |||
| 56 | #flash img { | 152 | #flash img { |
| 57 | height: 12px; | 153 | height: 12px; |
| 58 | width: 12px; | 154 | width: 12px; |
| @@ -68,62 +164,87 @@ span#flash_error, span.warning { | |||
| 68 | padding-bottom: 1px; | 164 | padding-bottom: 1px; |
| 69 | } | 165 | } |
| 70 | 166 | ||
| 71 | /* Pagination */ | 167 | span.warning a { |
| 168 | color: inherit; | ||
| 169 | text-decoration: underline; | ||
| 170 | } | ||
| 72 | 171 | ||
| 73 | div.pagination { | 172 | .error_messages { |
| 74 | padding-top: 5px; | 173 | border-left: 3px solid #cc0000; |
| 75 | padding-bottom: 15px; | 174 | background-color: #fdecea; |
| 175 | padding: 6px 10px; | ||
| 176 | margin-bottom: 1rem; | ||
| 76 | } | 177 | } |
| 77 | 178 | ||
| 78 | div.pagination span.current, div.pagination a:hover { | 179 | .error_messages ul { |
| 79 | color: #ff9600; | 180 | margin: 0; |
| 181 | padding-left: 1.25rem; | ||
| 80 | } | 182 | } |
| 81 | 183 | ||
| 82 | /* Styles */ | 184 | /* ============================================================ |
| 185 | Pagination | ||
| 186 | ============================================================ */ | ||
| 83 | 187 | ||
| 84 | .right { | 188 | div.pagination { |
| 85 | text-align: right; | 189 | padding-top: 5px; |
| 190 | padding-bottom: 15px; | ||
| 191 | display: flex; | ||
| 192 | align-items: center; | ||
| 193 | gap: 0.35rem; | ||
| 86 | } | 194 | } |
| 87 | 195 | ||
| 88 | .clear_left { | 196 | div.pagination a, |
| 89 | clear: left; | 197 | div.pagination em.current, |
| 198 | div.pagination span.gap { | ||
| 199 | display: inline-flex; | ||
| 200 | align-items: center; | ||
| 201 | justify-content: center; | ||
| 202 | min-width: 1.75rem; | ||
| 203 | height: 1.75rem; | ||
| 204 | padding: 0 0.4rem; | ||
| 205 | border-radius: 4px; | ||
| 206 | font-style: normal; | ||
| 90 | } | 207 | } |
| 91 | 208 | ||
| 92 | #navigation { | 209 | div.pagination a:hover { |
| 93 | position: relative; | 210 | color: #ff9600; |
| 94 | margin-top: 10px; | ||
| 95 | } | 211 | } |
| 96 | 212 | ||
| 97 | #navigation div { | 213 | div.pagination em.current { |
| 98 | float: left; | 214 | background-color: #ff9600; |
| 215 | color: #ffffff; | ||
| 216 | font-weight: bold; | ||
| 99 | } | 217 | } |
| 100 | 218 | ||
| 101 | #navigation a:hover { | 219 | div.pagination span.gap { |
| 102 | color: #ffffff; | 220 | color: #969696; |
| 103 | background-color: #000000; | ||
| 104 | } | 221 | } |
| 105 | 222 | ||
| 106 | #main_navigation a, | 223 | div.pagination .previous_page.disabled, |
| 107 | #sub_navigation a, | 224 | div.pagination .next_page.disabled { |
| 108 | #metadata a, | 225 | color: #969696; |
| 109 | input[type=submit], | 226 | cursor: not-allowed; |
| 110 | #overview_toggle a { | ||
| 111 | letter-spacing: 1px; | ||
| 112 | padding-left: 5px; | ||
| 113 | padding-right: 5px; | ||
| 114 | padding-top: 1px; | ||
| 115 | padding-bottom: 1px; | ||
| 116 | text-transform: lowercase; | ||
| 117 | } | 227 | } |
| 118 | 228 | ||
| 119 | /* button_to forms: render submit inputs as plain links */ | 229 | /* ============================================================ |
| 230 | Buttons | ||
| 231 | ============================================================ */ | ||
| 232 | |||
| 233 | /* button_to forms: render submit inputs/buttons as plain links, | ||
| 234 | orange on hover; .destructive variant is red. */ | ||
| 120 | form.button_to { | 235 | form.button_to { |
| 121 | display: inline; | 236 | display: inline; |
| 122 | margin: 0; | 237 | margin: 0; |
| 123 | padding: 0; | 238 | padding: 0; |
| 124 | } | 239 | } |
| 125 | 240 | ||
| 126 | form.button_to input[type="submit"] { | 241 | form.button_to input[type="submit"], |
| 242 | form.button_to button[type="submit"] { | ||
| 243 | display: inline-flex; | ||
| 244 | align-items: center; | ||
| 245 | gap: 0.5rem; | ||
| 246 | -webkit-appearance: none; | ||
| 247 | appearance: none; | ||
| 127 | background: none; | 248 | background: none; |
| 128 | border: none; | 249 | border: none; |
| 129 | padding: 0; | 250 | padding: 0; |
| @@ -131,80 +252,231 @@ form.button_to input[type="submit"] { | |||
| 131 | font: inherit; | 252 | font: inherit; |
| 132 | color: inherit; | 253 | color: inherit; |
| 133 | cursor: pointer; | 254 | cursor: pointer; |
| 134 | text-decoration: none | 255 | text-decoration: none; |
| 135 | } | 256 | } |
| 136 | 257 | ||
| 137 | form.button_to input[type="submit"]:hover { | 258 | form.button_to.state_changing input[type="submit"], |
| 259 | form.button_to.state_changing button[type="submit"] { | ||
| 260 | color: #0d47a1; | ||
| 261 | background-color: #e3f2fd; | ||
| 262 | } | ||
| 263 | |||
| 264 | form.button_to.state_changing input[type="submit"]:hover, | ||
| 265 | form.button_to.state_changing button[type="submit"]:hover { | ||
| 138 | color: #ffffff; | 266 | color: #ffffff; |
| 139 | background-color: #ff9600; | 267 | background-color: #1565c0; |
| 140 | } | 268 | } |
| 141 | 269 | ||
| 142 | #admin_wizard { | 270 | form.button_to.destructive input[type="submit"], |
| 143 | margin-bottom: 2rem; | 271 | form.button_to.destructive button[type="submit"] { |
| 272 | color: #cc0000; | ||
| 273 | background-color: #fdecea; | ||
| 144 | } | 274 | } |
| 145 | 275 | ||
| 146 | #page_submit a, | 276 | form.button_to.destructive input[type="submit"]:hover, |
| 147 | .admin_wizard_button a { | 277 | form.button_to.destructive button[type="submit"]:hover { |
| 148 | color: white !important; | 278 | color: #ffffff; |
| 279 | background-color: #cc0000; | ||
| 149 | } | 280 | } |
| 150 | 281 | ||
| 151 | #page_submit, | 282 | form.button_to.computation input[type="submit"], |
| 152 | .admin_wizard_button { | 283 | form.button_to.computation button[type="submit"] { |
| 153 | display: inline-block; | 284 | color: #00838f; |
| 154 | font-size: 1rem; | 285 | background-color: #e0f2f1; |
| 155 | font-weight: bold; | ||
| 156 | padding: 0.5rem; | ||
| 157 | margin-bottom: 0.5rem; | ||
| 158 | background-color: green; | ||
| 159 | } | 286 | } |
| 160 | 287 | ||
| 161 | #page_submit:hover, | 288 | form.button_to.computation input[type="submit"]:hover, |
| 162 | .admin_wizard_button:hover { | 289 | form.button_to.computation button[type="submit"]:hover { |
| 163 | background-color: lime !important; | 290 | color: #ffffff; |
| 291 | background-color: #00838f; | ||
| 164 | } | 292 | } |
| 165 | 293 | ||
| 166 | #sub_navigation a { | 294 | form.button_to.computation input[type="submit"]:disabled, |
| 167 | color: #969696; | 295 | form.button_to.computation button[type="submit"]:disabled { |
| 296 | opacity: 0.4; | ||
| 297 | cursor: not-allowed; | ||
| 298 | pointer-events: none; | ||
| 168 | } | 299 | } |
| 169 | 300 | ||
| 170 | #sub_navigation a:hover, #metadata a:hover { | 301 | /* Native (non-button_to) submit buttons -- Create/Save/Publish forms. |
| 171 | color: #ffffff; | 302 | Lower specificity than form.button_to's own rule, so button_to forms |
| 172 | background-color: #ff9600; | 303 | are correctly unaffected. If a button_to output ever loses its class |
| 304 | wrapper, it will silently fall through to this bordered style instead | ||
| 305 | of rendering as a plain link -- worth knowing, not necessarily fixing. */ | ||
| 306 | input[type="submit"] { | ||
| 307 | -webkit-appearance: none; | ||
| 308 | appearance: none; | ||
| 309 | background: none; | ||
| 310 | border: 1px solid #000000; | ||
| 311 | border-radius: 2px; | ||
| 312 | padding: 4px 12px; | ||
| 313 | font: inherit; | ||
| 314 | font-weight: bold; | ||
| 315 | color: inherit; | ||
| 316 | cursor: pointer; | ||
| 317 | text-decoration: none; | ||
| 173 | } | 318 | } |
| 174 | 319 | ||
| 175 | #sub_navigation a.selected { | 320 | input[type="submit"]:hover { |
| 176 | color: #ffffff; | 321 | color: #ffffff; |
| 177 | background-color: #ff9600; | 322 | background-color: #000000; |
| 178 | } | 323 | } |
| 179 | 324 | ||
| 180 | #navigation a.selected, #metadata a.selected, #overview_toggle a.selected { | 325 | /* ============================================================ |
| 181 | color: #ffffff; | 326 | Admin dashboard |
| 327 | ============================================================ */ | ||
| 328 | |||
| 329 | #main_navigation { | ||
| 330 | display: flex; | ||
| 331 | align-items: center; | ||
| 332 | gap: 0.5rem; | ||
| 333 | } | ||
| 334 | |||
| 335 | #main_navigation a, | ||
| 336 | #main_navigation form.button_to button { | ||
| 337 | display: inline-flex; | ||
| 338 | text-decoration: none; | ||
| 339 | align-items: center; | ||
| 340 | background-color: #f7f7f7; | ||
| 341 | justify-content: center; | ||
| 342 | width: 2rem; | ||
| 343 | height: 2rem; | ||
| 344 | padding: 0; | ||
| 345 | border-radius: 2px; | ||
| 346 | } | ||
| 347 | |||
| 348 | #main_navigation a:hover, | ||
| 349 | #main_navigation form.button_to button:hover { | ||
| 182 | background-color: #000000; | 350 | background-color: #000000; |
| 351 | color: #ffffff; | ||
| 352 | } | ||
| 353 | |||
| 354 | #main_navigation svg, | ||
| 355 | .action_button svg, | ||
| 356 | form.button_to svg { | ||
| 357 | width: 1.25rem; | ||
| 358 | height: 1.25rem; | ||
| 359 | } | ||
| 360 | |||
| 361 | #dashboard_search { | ||
| 362 | margin-bottom: 1.5rem; | ||
| 363 | } | ||
| 364 | |||
| 365 | #dashboard_search input[type=text] { | ||
| 366 | width: 100%; | ||
| 367 | box-sizing: border-box; | ||
| 368 | padding: 10px 14px; | ||
| 369 | font-size: 1.1rem; | ||
| 370 | border-radius: 2px; | ||
| 371 | } | ||
| 372 | |||
| 373 | .button_row { | ||
| 374 | display: flex; | ||
| 375 | flex-wrap: wrap; | ||
| 376 | gap: 0.75rem; | ||
| 377 | align-items: center; | ||
| 378 | } | ||
| 379 | |||
| 380 | .button_row .action_button, | ||
| 381 | .node_search_form .action_button { | ||
| 382 | margin-bottom: 0; | ||
| 383 | } | ||
| 384 | |||
| 385 | #dashboard_signposts { | ||
| 386 | margin-bottom: 2rem; | ||
| 387 | } | ||
| 388 | |||
| 389 | #dashboard_housekeeping h3 { | ||
| 390 | margin-bottom: 0.75rem; | ||
| 391 | } | ||
| 392 | |||
| 393 | #dashboard_widgets { | ||
| 394 | display: flex; | ||
| 395 | flex-wrap: wrap; | ||
| 396 | gap: 1.5rem; | ||
| 397 | margin-bottom: 2rem; | ||
| 398 | } | ||
| 399 | |||
| 400 | .dashboard_widget { | ||
| 401 | flex: 1 1 300px; | ||
| 402 | border: 1px solid #e8e8e8; | ||
| 403 | border-radius: 6px; | ||
| 404 | padding: 0.5rem 0.75rem; | ||
| 405 | } | ||
| 406 | |||
| 407 | .dashboard_widget h3 { | ||
| 408 | margin-top: 0; | ||
| 183 | } | 409 | } |
| 184 | 410 | ||
| 185 | /* Nodes */ | 411 | .dashboard_widget ul { |
| 412 | list-style: none; | ||
| 413 | margin: 0 0 0.75rem; | ||
| 414 | padding: 0; | ||
| 415 | } | ||
| 416 | |||
| 417 | .dashboard_widget li { | ||
| 418 | display: flex; | ||
| 419 | justify-content: space-between; | ||
| 420 | align-items: flex-start; | ||
| 421 | gap: 1rem; | ||
| 422 | padding: 0.35rem 0; | ||
| 423 | border-bottom: 1px solid #f1f1f1; | ||
| 424 | } | ||
| 186 | 425 | ||
| 187 | table.node_table { | 426 | .dashboard_widget_meta { |
| 427 | white-space: nowrap; | ||
| 428 | color: #969696; | ||
| 429 | font-size: 0.85rem; | ||
| 430 | } | ||
| 431 | |||
| 432 | /* ============================================================ | ||
| 433 | Tables | ||
| 434 | ============================================================ */ | ||
| 435 | |||
| 436 | /* Generic fallback header row. Shadowed by table.node_table's own | ||
| 437 | tr.header (2rem) and duplicated (same values) by table#revisions's | ||
| 438 | own tr.header below. Applies as-is to #menu_item_list, | ||
| 439 | div#draft_list table, and table.user_table, none of which define | ||
| 440 | their own tr.header. */ | ||
| 441 | table tr.header { | ||
| 442 | height: 20px; | ||
| 443 | text-align: left; | ||
| 444 | } | ||
| 445 | |||
| 446 | table.node_table , | ||
| 447 | table.assets_table, | ||
| 448 | table.user_table, | ||
| 449 | table.events_table { | ||
| 188 | border-collapse: collapse; | 450 | border-collapse: collapse; |
| 189 | } | 451 | } |
| 190 | 452 | ||
| 191 | table.node_table tr { | 453 | table.node_table tr { |
| 192 | min-height: 2rem; | ||
| 193 | border-bottom: 1px solid #000000; | 454 | border-bottom: 1px solid #000000; |
| 194 | } | 455 | } |
| 195 | 456 | ||
| 457 | table.assets_table tr, | ||
| 458 | table.user_table tr, | ||
| 459 | table.events_table tr { | ||
| 460 | border-bottom: 1px solid #e8e8e8; | ||
| 461 | } | ||
| 462 | |||
| 196 | table.node_table th.node_id, | 463 | table.node_table th.node_id, |
| 197 | table.node_table th.revision, | 464 | table.node_table th.revision { |
| 198 | table.node_table th.title { | ||
| 199 | min-width: 4rem; | 465 | min-width: 4rem; |
| 200 | } | 466 | } |
| 201 | 467 | ||
| 468 | /* th.title's min-width is set to 4rem above and 12rem here -- the | ||
| 469 | later, equally-specific rule wins, so th.title ends up at 12rem and | ||
| 470 | the 4rem entry for it above is dead. Likely a copy/paste leftover. */ | ||
| 202 | table.node_table th.editor, | 471 | table.node_table th.editor, |
| 203 | table.node_table th.title { | 472 | table.node_table th.title { |
| 204 | min-width: 12rem; | 473 | min-width: 12rem; |
| 205 | } | 474 | } |
| 206 | 475 | ||
| 207 | table.node_table tr.header { | 476 | table.node_table tr.header, |
| 477 | table.assets_table tr.header, | ||
| 478 | table.user_table tr.header, | ||
| 479 | table.events_table tr.header { | ||
| 208 | height: 2rem; | 480 | height: 2rem; |
| 209 | text-align: left; | 481 | text-align: left; |
| 210 | } | 482 | } |
| @@ -217,193 +489,389 @@ table.node_table td { | |||
| 217 | min-width: 2rem; | 489 | min-width: 2rem; |
| 218 | } | 490 | } |
| 219 | 491 | ||
| 492 | table.assets_table td, | ||
| 493 | table.user_table td, | ||
| 494 | table.events_table td { | ||
| 495 | padding-top: 8px; | ||
| 496 | padding-bottom: 8px; | ||
| 497 | padding-right: 25px; | ||
| 498 | padding-left: 0px; | ||
| 499 | min-width: 2rem; | ||
| 500 | } | ||
| 501 | |||
| 220 | table.node_table .node_id { | 502 | table.node_table .node_id { |
| 221 | padding-left: 10px; | 503 | padding-left: 10px; |
| 222 | padding-right: 15px; | 504 | padding-right: 15px; |
| 223 | } | 505 | } |
| 224 | 506 | ||
| 225 | table.node_table tr.header:hover { | 507 | table.node_table .actions { |
| 226 | background-color: #ffffff; | 508 | text-transform: lowercase; |
| 227 | } | 509 | } |
| 228 | 510 | ||
| 229 | table.node_table tr:hover { | 511 | table.node_table tr.header:hover, |
| 230 | background-color: #f1f1f1; | 512 | table.assets_table tr.header:hover, |
| 513 | table.user_table tr.header:hover, | ||
| 514 | table.events_table tr.header:hover { | ||
| 515 | background-color: #ffffff; | ||
| 231 | } | 516 | } |
| 232 | 517 | ||
| 233 | table.node_table .actions { | 518 | table.node_table tr:hover, |
| 234 | text-transform: lowercase; | 519 | table.assets_table tr:hover, |
| 520 | table.user_table tr:hover, | ||
| 521 | table.events_table tr:hover { | ||
| 522 | background-color: #f1f1f1; | ||
| 235 | } | 523 | } |
| 236 | 524 | ||
| 237 | #admin_sitemap_table .node_id:before { | 525 | #admin_sitemap_table .node_id:before { |
| 238 | content: "• "; | 526 | content: "• "; |
| 239 | } | 527 | } |
| 240 | 528 | ||
| 241 | /* Revisions */ | 529 | table.revisions_table { |
| 242 | |||
| 243 | table#revisions { | ||
| 244 | border-collapse: collapse; | 530 | border-collapse: collapse; |
| 245 | } | 531 | } |
| 246 | 532 | ||
| 247 | table#revisions td { | 533 | table.revisions_table thead { |
| 248 | padding-right: 25px; | 534 | position: sticky; |
| 535 | top: 0; | ||
| 536 | z-index: 1; | ||
| 249 | } | 537 | } |
| 250 | 538 | ||
| 251 | table#revisions td.title { | 539 | table.revisions_table th, |
| 540 | table.revisions_table td { | ||
| 541 | padding: 0.4rem 0.75rem; | ||
| 542 | text-align: left; | ||
| 543 | } | ||
| 544 | |||
| 545 | table.revisions_table thead tr.header, | ||
| 546 | table.revisions_table thead tr.diff_sticky_bar { | ||
| 547 | background-color: #ffffff; | ||
| 548 | } | ||
| 549 | |||
| 550 | table.revisions_table .diff_sticky_bar td { | ||
| 551 | padding-top: 0.5rem; | ||
| 552 | padding-bottom: 0.5rem; | ||
| 553 | border-bottom: 1px solid #e8e8e8; | ||
| 554 | } | ||
| 555 | |||
| 556 | #diff_selection_label { | ||
| 557 | margin-left: 0.75rem; | ||
| 558 | color: #969696; | ||
| 559 | } | ||
| 560 | table.revisions_table td.title { | ||
| 252 | width: 450px; | 561 | width: 450px; |
| 253 | } | 562 | } |
| 254 | 563 | ||
| 255 | table#revisions td.date { | 564 | table.revisions_table td.date { |
| 256 | width: 175px; | 565 | width: 175px; |
| 257 | } | 566 | } |
| 258 | 567 | ||
| 259 | table#revisions tr.header:hover { | 568 | table.revisions_table tr:not(.header):not(.no_hover) { |
| 569 | border-bottom: 1px solid #e8e8e8; | ||
| 570 | } | ||
| 571 | |||
| 572 | table.revisions_table tr.header:hover { | ||
| 260 | background-color: #ffffff; | 573 | background-color: #ffffff; |
| 261 | } | 574 | } |
| 262 | 575 | ||
| 263 | table#revisions tr.no_hover:hover { | 576 | table.revisions_table tr.no_hover:hover { |
| 264 | background-color: #ffffff; | 577 | background-color: #ffffff; |
| 265 | } | 578 | } |
| 266 | 579 | ||
| 267 | table#revisions tr:hover { | 580 | table.revisions_table tr:hover { |
| 268 | background-color: #f1f1f1; | 581 | background-color: #f1f1f1; |
| 269 | } | 582 | } |
| 270 | 583 | ||
| 271 | table#revisions tr.header { | 584 | .events_table .rrule_text { |
| 272 | height: 20px; | 585 | display: inline-block; |
| 273 | text-align: left; | 586 | max-width: 300px; |
| 587 | overflow-wrap: break-word; | ||
| 274 | } | 588 | } |
| 275 | 589 | ||
| 276 | table#revisions tr { | 590 | .events_table .truncate { |
| 277 | height: 45px | 591 | display: inline-block; |
| 592 | max-width: 200px; | ||
| 593 | overflow: hidden; | ||
| 594 | text-overflow: ellipsis; | ||
| 595 | white-space: nowrap; | ||
| 596 | vertical-align: bottom; | ||
| 278 | } | 597 | } |
| 279 | 598 | ||
| 280 | /* Page Editor */ | 599 | #diffview del { |
| 600 | background: #ffd7d5; | ||
| 601 | color: #82071e; | ||
| 602 | text-decoration: line-through; | ||
| 603 | padding: 0 2px; | ||
| 604 | border-radius: 2px; | ||
| 605 | } | ||
| 281 | 606 | ||
| 282 | input[type=text], textarea { | 607 | #diffview ins { |
| 283 | font-size: 1rem; | 608 | background: #ccffd8; |
| 284 | font-family: Helvetica; | 609 | color: #055d20; |
| 285 | border: 1px solid #989898; | 610 | text-decoration: none; |
| 611 | padding: 0 2px; | ||
| 612 | border-radius: 2px; | ||
| 286 | } | 613 | } |
| 287 | 614 | ||
| 288 | select { | 615 | #diffview .diff_side_by_side { |
| 289 | font-size: 1rem; | 616 | display: flex; |
| 290 | border: 1px solid #989898; | 617 | gap: 1rem; |
| 291 | } | 618 | } |
| 292 | 619 | ||
| 293 | input[type=text]#tag_list, | 620 | #diffview .diff_column { |
| 294 | input[type=text]#node_staged_slug, | 621 | flex: 1; |
| 295 | input#move_to_search_term { | ||
| 296 | padding: 5px; | ||
| 297 | } | 622 | } |
| 298 | 623 | ||
| 299 | input[type=password] { | 624 | #diffview del.diff_structural, |
| 300 | border: 1px solid #989898; | 625 | #diffview ins.diff_structural { |
| 626 | cursor: help; | ||
| 301 | } | 627 | } |
| 302 | 628 | ||
| 303 | input[type=submit] { | 629 | .diff_unchanged { |
| 304 | border: none; | 630 | color: #969696; |
| 305 | padding-top: 0px; | 631 | font-style: italic; |
| 306 | padding-bottom: 1px; | ||
| 307 | background-color: #ffffff; | ||
| 308 | } | 632 | } |
| 309 | 633 | ||
| 310 | input[type=submit]:hover { | 634 | .diff_set_list { |
| 311 | border: none; | 635 | list-style: none; |
| 312 | color: #ffffff; | 636 | padding-left: 0; |
| 313 | background-color: #ff9600; | 637 | margin: 0; |
| 314 | } | 638 | } |
| 315 | 639 | ||
| 316 | input[type=radio] { | 640 | /* ============================================================ |
| 317 | border: 1px solid #989898; | 641 | Translation compare view (page_translations#show) |
| 642 | ============================================================ */ | ||
| 643 | |||
| 644 | .translation_compare { | ||
| 645 | display: flex; | ||
| 646 | flex-direction: column; | ||
| 647 | gap: 1.5rem; | ||
| 318 | } | 648 | } |
| 319 | 649 | ||
| 320 | div#login_form input[type=text], div#login_form input[type=password] { | 650 | @media(min-width:1016px) { |
| 321 | width: 150px; | 651 | .translation_compare { |
| 652 | flex-direction: row; | ||
| 653 | gap: 2rem; | ||
| 654 | } | ||
| 655 | |||
| 656 | .translation_compare_column { | ||
| 657 | flex: 1; | ||
| 658 | min-width: 0; | ||
| 659 | } | ||
| 322 | } | 660 | } |
| 323 | 661 | ||
| 662 | table.user_table td.user_login { | ||
| 663 | padding-right: 30px; | ||
| 664 | } | ||
| 665 | |||
| 666 | /* ============================================================ | ||
| 667 | Node display (nodes#show / nodes#new) | ||
| 668 | ============================================================ */ | ||
| 324 | 669 | ||
| 325 | @media(min-width:1016px) { | 670 | @media(min-width:1016px) { |
| 326 | input#tag_list, | 671 | div.node_description { |
| 327 | input#node_staged_slug, | 672 | float: left; |
| 328 | input#move_to_search_term { | 673 | width: 110px; |
| 329 | width: 690px; | 674 | min-height: 2rem; |
| 675 | text-align: right; | ||
| 676 | text-transform: lowercase; | ||
| 677 | vertical-align: top; | ||
| 678 | } | ||
| 679 | |||
| 680 | div.node_content { | ||
| 681 | margin-left: 120px; | ||
| 682 | min-height: 2rem; | ||
| 683 | display: block; | ||
| 684 | margin-bottom: 1rem; | ||
| 685 | } | ||
| 330 | } | 686 | } |
| 687 | @media(max-width:1015px) { | ||
| 688 | div.node_description { | ||
| 689 | min-height: 2rem; | ||
| 690 | width: 30em; | ||
| 691 | text-transform: lowercase; | ||
| 692 | vertical-align: top; | ||
| 693 | } | ||
| 331 | 694 | ||
| 332 | input[type=text]#page_title { | 695 | div.node_content { |
| 333 | width: 690px; | 696 | min-height: 2rem; |
| 334 | font-size: 1rem; | 697 | margin-bottom: 1rem; |
| 335 | padding-top: 6px; | 698 | } |
| 336 | padding-bottom: 4px; | 699 | } |
| 337 | padding-left: 5px; | 700 | |
| 338 | padding-right: 5px; | 701 | .node_content.node_info_group { |
| 702 | border: 1px solid #e8e8e8; | ||
| 703 | border-radius: 6px; | ||
| 704 | padding: 0.5rem 0.75rem; | ||
| 705 | } | ||
| 706 | |||
| 707 | .node_info_group .disabled_action { | ||
| 708 | display: inline-block; | ||
| 709 | border: 1px solid #c0c0c0; | ||
| 710 | border-radius: 2px; | ||
| 711 | padding: 4px 12px; | ||
| 339 | font-weight: bold; | 712 | font-weight: bold; |
| 713 | color: #969696; | ||
| 714 | cursor: not-allowed; | ||
| 340 | } | 715 | } |
| 341 | 716 | ||
| 342 | textarea#page_abstract { | 717 | .node_info_group_items { |
| 343 | width: 690px; | 718 | display: flex; |
| 344 | height: 150px; | 719 | flex-wrap: wrap; |
| 345 | padding: 5px; | 720 | gap: 0.5rem 2rem; |
| 346 | } | 721 | } |
| 347 | 722 | ||
| 348 | #page_editor textarea#page_body { | 723 | .node_info_item { |
| 349 | width: 700px; | 724 | min-width: 140px; |
| 350 | height: 600px; | ||
| 351 | } | 725 | } |
| 352 | 726 | ||
| 353 | #page_editor #metadata, #page_editor #content, #admin_overview { | 727 | .node_info_label { |
| 354 | margin-left: -125px; | 728 | display: block; |
| 729 | font-weight: normal; | ||
| 730 | color: #969696; | ||
| 731 | font-size: 0.85rem; | ||
| 732 | text-transform: lowercase; | ||
| 355 | } | 733 | } |
| 356 | 734 | ||
| 735 | .node_info_group ul { | ||
| 736 | margin: 0; | ||
| 737 | padding-left: 1.25rem; | ||
| 357 | } | 738 | } |
| 358 | 739 | ||
| 359 | /* Mobile */ | 740 | .node_info_group li { |
| 360 | @media(max-width:1015px) { | 741 | margin-bottom: 0.25rem; |
| 361 | input#tag_list, | ||
| 362 | input#node_staged_slug, | ||
| 363 | input#move_to_search_term { | ||
| 364 | box-sizing: border-box; | ||
| 365 | width: 100%; | ||
| 366 | } | 742 | } |
| 367 | 743 | ||
| 368 | #page_editor #content, | 744 | .node_content.node_status { |
| 369 | #page_editor #metadata { | 745 | border: 2px solid #000000; |
| 370 | padding-right: 5px; | 746 | background-color: #fafafa; |
| 371 | } | 747 | } |
| 372 | 748 | ||
| 373 | #page_editor textarea#page_body { | 749 | .node_status .node_info_group_items:first-child { |
| 750 | margin-bottom: 0.75rem; | ||
| 751 | } | ||
| 752 | |||
| 753 | .node_status .node_info_group_items + .node_info_group_items { | ||
| 754 | margin-top: 0.5rem; | ||
| 755 | margin-bottom: 0.5rem; | ||
| 756 | } | ||
| 757 | |||
| 758 | .node_status .field_hint { | ||
| 759 | margin-top: 0.5rem; | ||
| 760 | display: block; | ||
| 761 | } | ||
| 762 | |||
| 763 | .action_button, | ||
| 764 | form.button_to input[type="submit"], | ||
| 765 | form.button_to button[type="submit"] { | ||
| 766 | padding: 4px 12px; | ||
| 767 | line-height: 1.2; | ||
| 374 | box-sizing: border-box; | 768 | box-sizing: border-box; |
| 375 | width: 800px; | ||
| 376 | height: 50rem; | ||
| 377 | } | 769 | } |
| 378 | 770 | ||
| 379 | #page_editor textarea#page_abstract { | 771 | form.button_to input[type="submit"], |
| 772 | form.button_to button[type="submit"] { | ||
| 773 | border: 1px solid transparent; | ||
| 774 | border-radius: 2px; | ||
| 775 | } | ||
| 776 | |||
| 777 | .field_hint { | ||
| 778 | display: block; | ||
| 779 | margin-top: 2px; | ||
| 780 | font-size: 0.75rem; | ||
| 781 | color: #969696; | ||
| 782 | padding-bottom: 4px; | ||
| 783 | } | ||
| 784 | |||
| 785 | .action_button + .field_hint { | ||
| 786 | display: inline; | ||
| 787 | margin-left: 0.5rem; | ||
| 788 | margin-top: 0; | ||
| 789 | } | ||
| 790 | |||
| 791 | .revisions_full_history_link { | ||
| 792 | margin-bottom: 0; | ||
| 793 | } | ||
| 794 | |||
| 795 | .node_search_form { | ||
| 796 | display: flex; | ||
| 797 | align-items: center; | ||
| 798 | gap: 0.5rem; | ||
| 799 | margin-bottom: 0.75rem; | ||
| 800 | } | ||
| 801 | |||
| 802 | .node_search_form input[type=text] { | ||
| 803 | padding: 4px 12px; | ||
| 380 | box-sizing: border-box; | 804 | box-sizing: border-box; |
| 381 | height: 8rem; | 805 | height: 2.25rem; |
| 382 | width: 100%; | 806 | width: 22rem; |
| 807 | border-radius: 2px; | ||
| 383 | } | 808 | } |
| 384 | 809 | ||
| 385 | input[type=text]#page_title { | 810 | .node_search_form .action_button { |
| 811 | height: 2.25rem; | ||
| 386 | box-sizing: border-box; | 812 | box-sizing: border-box; |
| 387 | width: 100%; | ||
| 388 | font-size: 2rem; | ||
| 389 | padding-top: 6px; | ||
| 390 | padding-bottom: 4px; | ||
| 391 | padding-left: 5px; | ||
| 392 | font-weight: bold; | ||
| 393 | } | 813 | } |
| 394 | 814 | ||
| 395 | input[type=text], textarea { | 815 | /* Layout only -- the at-rest visibility (wavy underline) for these |
| 396 | font-size: 1.5rem; | 816 | links comes from the scoped rule in Base elements above. */ |
| 817 | .add_child_links { | ||
| 818 | margin-top: 0.5rem; | ||
| 397 | } | 819 | } |
| 398 | select { | 820 | |
| 399 | font-size: 1.5rem; | 821 | .add_child_links a { |
| 822 | white-space: nowrap; | ||
| 400 | } | 823 | } |
| 401 | 824 | ||
| 402 | #metadata ul#image_box { | 825 | .sitemap_node { |
| 403 | width: 100% !important; | 826 | padding-bottom: 0.5rem; |
| 827 | margin-bottom: 0.5rem; | ||
| 828 | border-bottom: 1px solid #ececec; | ||
| 404 | } | 829 | } |
| 830 | |||
| 831 | .sitemap_node h4 { | ||
| 832 | margin: 0; | ||
| 833 | } | ||
| 834 | |||
| 835 | .sitemap_node .field_hint { | ||
| 836 | display: block; | ||
| 837 | margin: 2px 0 0; | ||
| 405 | } | 838 | } |
| 406 | 839 | ||
| 840 | .sitemap_node p { | ||
| 841 | margin: 2px 0 0; | ||
| 842 | } | ||
| 843 | |||
| 844 | #sitemap details details { | ||
| 845 | margin-left: 1.5rem; | ||
| 846 | border-left: 1px solid #e8e8e8; | ||
| 847 | padding-left: 0.75rem; | ||
| 848 | } | ||
| 849 | |||
| 850 | #sitemap summary { | ||
| 851 | padding: 4px 0; | ||
| 852 | } | ||
| 853 | |||
| 854 | #sitemap details:not([open]) > summary { | ||
| 855 | color: #ff9600; | ||
| 856 | font-weight: bold; | ||
| 857 | } | ||
| 858 | |||
| 859 | .aligned_action_row { | ||
| 860 | display: flex; | ||
| 861 | flex-wrap: wrap; | ||
| 862 | align-items: start; | ||
| 863 | gap: 0.5rem; | ||
| 864 | } | ||
| 865 | |||
| 866 | .aligned_action_row .action_button, | ||
| 867 | .aligned_action_row form.button_to { | ||
| 868 | margin-top: -5px; | ||
| 869 | } | ||
| 870 | |||
| 871 | /* ============================================================ | ||
| 872 | Page editor / metadata forms | ||
| 873 | ============================================================ */ | ||
| 874 | |||
| 407 | #new_node { | 875 | #new_node { |
| 408 | margin-left: -118px; | 876 | margin-left: -118px; |
| 409 | } | 877 | } |
| @@ -412,51 +880,158 @@ select { | |||
| 412 | width: 350px; | 880 | width: 350px; |
| 413 | } | 881 | } |
| 414 | 882 | ||
| 415 | tr {} | 883 | div#login_form input[type=text], div#login_form input[type=password] { |
| 884 | width: 150px; | ||
| 885 | } | ||
| 416 | 886 | ||
| 417 | td { | 887 | input[type=text]#tag_list, |
| 418 | padding-top: 10px; | 888 | input[type=text]#node_staged_slug, |
| 419 | padding-bottom: 5px; | 889 | input#move_to_search_term { |
| 420 | padding-right: 10px; | 890 | padding: 5px; |
| 421 | } | 891 | } |
| 422 | 892 | ||
| 423 | @media(min-width:1016px) { | 893 | div#page_editor { |
| 424 | div.node_description { | 894 | margin-top: 0px; |
| 425 | float: left; | 895 | margin-left: 10px; |
| 426 | width: 110px; | ||
| 427 | min-height: 2rem; | ||
| 428 | text-align: right; | ||
| 429 | text-transform: lowercase; | ||
| 430 | vertical-align: top; | ||
| 431 | } | 896 | } |
| 432 | 897 | ||
| 433 | div.node_content { | 898 | #page_editor a.action_button, |
| 434 | margin-left: 120px; | 899 | .action_button { |
| 435 | min-height: 2rem; | 900 | display: inline-flex; |
| 436 | display: block; | 901 | align-items: center; |
| 902 | gap: 0.5rem; | ||
| 903 | -webkit-appearance: none; | ||
| 904 | appearance: none; | ||
| 905 | border: 1px solid #000000; | ||
| 906 | border-radius: 2px; | ||
| 907 | padding: 4px 12px; | ||
| 908 | font-weight: bold; | ||
| 909 | text-decoration: none; | ||
| 910 | color: #000000; | ||
| 437 | margin-bottom: 1rem; | 911 | margin-bottom: 1rem; |
| 912 | background: none; | ||
| 913 | cursor: pointer; | ||
| 914 | font: inherit; | ||
| 438 | } | 915 | } |
| 439 | } | 916 | |
| 440 | @media(max-width:1015px) { | 917 | #page_editor a.action_button:hover, |
| 441 | div.node_description { | 918 | .action_button:hover { |
| 442 | min-height: 2rem; | 919 | color: #ffffff; |
| 443 | width: 30em; | 920 | background-color: #000000; |
| 444 | text-transform: lowercase; | ||
| 445 | vertical-align: top; | ||
| 446 | } | 921 | } |
| 447 | 922 | ||
| 448 | div.node_content { | 923 | #page_editor input[type=text], |
| 449 | min-height: 2rem; | 924 | #page_editor input[type=password], |
| 450 | margin-bottom: 1rem; | 925 | #page_editor textarea { |
| 926 | padding: 5px; | ||
| 451 | } | 927 | } |
| 452 | 928 | ||
| 929 | @media(min-width:1016px) { | ||
| 930 | input#tag_list, | ||
| 931 | input#menu_search_term, | ||
| 932 | input#menu_item_node_id, | ||
| 933 | input#menu_item_path, | ||
| 934 | input#menu_item_title, | ||
| 935 | input#node_staged_slug, | ||
| 936 | input#move_to_search_term { | ||
| 937 | width: 690px; | ||
| 938 | } | ||
| 939 | |||
| 940 | input[type=text]#page_title { | ||
| 941 | width: 690px; | ||
| 942 | font-size: 1rem; | ||
| 943 | padding-top: 6px; | ||
| 944 | padding-bottom: 4px; | ||
| 945 | padding-left: 5px; | ||
| 946 | padding-right: 5px; | ||
| 947 | font-weight: bold; | ||
| 948 | } | ||
| 949 | |||
| 950 | textarea#page_abstract { | ||
| 951 | width: 690px; | ||
| 952 | height: 150px; | ||
| 953 | padding: 5px; | ||
| 954 | } | ||
| 955 | |||
| 956 | #page_editor textarea#page_body { | ||
| 957 | width: 700px; | ||
| 958 | height: 600px; | ||
| 959 | } | ||
| 960 | |||
| 961 | #page_editor #metadata, #page_editor #content, #admin_overview { | ||
| 962 | margin-left: -125px; | ||
| 963 | } | ||
| 964 | |||
| 965 | #page_editor input[type=text], | ||
| 966 | #page_editor input[type=password], | ||
| 967 | #page_editor textarea { | ||
| 968 | width: 690px; | ||
| 969 | } | ||
| 453 | } | 970 | } |
| 454 | 971 | ||
| 455 | div#page_editor { | 972 | @media(max-width:1015px) { |
| 456 | margin-top: 0px; | 973 | input#tag_list, |
| 457 | margin-left: 10px; | 974 | input#node_staged_slug, |
| 975 | input#move_to_search_term { | ||
| 976 | box-sizing: border-box; | ||
| 977 | width: 100%; | ||
| 978 | } | ||
| 979 | |||
| 980 | #page_editor #content, | ||
| 981 | #page_editor #metadata { | ||
| 982 | padding-right: 5px; | ||
| 983 | } | ||
| 984 | |||
| 985 | /* Fixed 800px width will overflow narrow viewports despite | ||
| 986 | border-box -- every sibling rule in this block uses 100% instead. | ||
| 987 | Worth checking on an actual phone; looks unfinished rather than | ||
| 988 | intentional. Not changed here since it wasn't part of what you | ||
| 989 | asked to fix. */ | ||
| 990 | #page_editor textarea#page_body { | ||
| 991 | box-sizing: border-box; | ||
| 992 | width: 800px; | ||
| 993 | height: 50rem; | ||
| 994 | } | ||
| 995 | |||
| 996 | #page_editor textarea#page_abstract { | ||
| 997 | box-sizing: border-box; | ||
| 998 | height: 8rem; | ||
| 999 | width: 100%; | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | input[type=text]#page_title { | ||
| 1003 | box-sizing: border-box; | ||
| 1004 | width: 100%; | ||
| 1005 | font-size: 2rem; | ||
| 1006 | padding-top: 6px; | ||
| 1007 | padding-bottom: 4px; | ||
| 1008 | padding-left: 5px; | ||
| 1009 | font-weight: bold; | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | #page_editor input[type=text], | ||
| 1013 | #page_editor input[type=password], | ||
| 1014 | #page_editor textarea { | ||
| 1015 | box-sizing: border-box; | ||
| 1016 | width: 100%; | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | input[type=text], textarea { | ||
| 1020 | font-size: 1.5rem; | ||
| 1021 | } | ||
| 1022 | select { | ||
| 1023 | font-size: 1.5rem; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | #metadata ul#image_box { | ||
| 1027 | width: 100% !important; | ||
| 1028 | } | ||
| 458 | } | 1029 | } |
| 459 | 1030 | ||
| 1031 | /* ============================================================ | ||
| 1032 | Draft list (dashboard widget) | ||
| 1033 | ============================================================ */ | ||
| 1034 | |||
| 460 | div#draft_list { | 1035 | div#draft_list { |
| 461 | border: 1px solid #000000; | 1036 | border: 1px solid #000000; |
| 462 | padding: 5px; | 1037 | padding: 5px; |
| @@ -472,32 +1047,61 @@ div#draft_list table tr.header:hover { | |||
| 472 | background-color: #ffffff; | 1047 | background-color: #ffffff; |
| 473 | } | 1048 | } |
| 474 | 1049 | ||
| 1050 | div#draft_list table tr:hover { | ||
| 1051 | background-color: #f1f1f1; | ||
| 1052 | } | ||
| 1053 | |||
| 475 | div#draft_list table td.actions { | 1054 | div#draft_list table td.actions { |
| 476 | text-transform: lowercase; | 1055 | text-transform: lowercase; |
| 477 | white-space: nowrap; | 1056 | white-space: nowrap; |
| 478 | } | 1057 | } |
| 479 | 1058 | ||
| 1059 | /* Layout only -- the at-rest visibility (wavy underline) for these | ||
| 1060 | links comes from the scoped rule in Base elements above. */ | ||
| 480 | div#draft_list table td.actions a { | 1061 | div#draft_list table td.actions a { |
| 481 | letter-spacing: 1px; | 1062 | letter-spacing: 1px; |
| 482 | padding-left: 5px; | 1063 | padding-left: 5px; |
| 483 | padding-right: 5px; | 1064 | padding-right: 5px; |
| 484 | } | 1065 | } |
| 485 | 1066 | ||
| 486 | div#draft_list table tr:hover { | 1067 | .search_results_list { |
| 487 | background-color: #f1f1f1; | 1068 | list-style: none; |
| 1069 | margin: 0; | ||
| 1070 | padding: 0; | ||
| 488 | } | 1071 | } |
| 489 | 1072 | ||
| 490 | table tr.header { | 1073 | .search_results_list li { |
| 491 | height: 20px; | 1074 | padding: 0.5rem 0; |
| 492 | text-align: left; | 1075 | border-bottom: 1px solid #f1f1f1; |
| 1076 | } | ||
| 1077 | |||
| 1078 | .search_tag_row { | ||
| 1079 | display: flex; | ||
| 1080 | flex-wrap: wrap; | ||
| 1081 | gap: 0.4rem; | ||
| 1082 | padding: 4px 4px 8px; | ||
| 1083 | border-bottom: 1px solid #e8e8e8; | ||
| 493 | } | 1084 | } |
| 494 | 1085 | ||
| 1086 | .search_tag_pill { | ||
| 1087 | display: inline-block; | ||
| 1088 | padding: 2px 10px; | ||
| 1089 | border-radius: 999px; | ||
| 1090 | background-color: #f1f1f1; | ||
| 1091 | font-size: 0.85rem; | ||
| 1092 | text-decoration: none; | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | /* ============================================================ | ||
| 1096 | Search widgets | ||
| 1097 | ============================================================ */ | ||
| 1098 | |||
| 495 | #search_widget { | 1099 | #search_widget { |
| 496 | position: absolute; | 1100 | position: absolute; |
| 497 | top: 20px; | 1101 | top: 2.75rem; |
| 498 | left: 50%; | 1102 | left: 0; |
| 499 | transform: translateX(-50%); | ||
| 500 | width: min(520px, 90vw); | 1103 | width: min(520px, 90vw); |
| 1104 | border-radius: 2px; | ||
| 501 | border: 1px solid #000000; | 1105 | border: 1px solid #000000; |
| 502 | -webkit-box-shadow: 3px 3px 5px #b1b1b1; | 1106 | -webkit-box-shadow: 3px 3px 5px #b1b1b1; |
| 503 | box-shadow: 3px 3px 5px #b1b1b1; | 1107 | box-shadow: 3px 3px 5px #b1b1b1; |
| @@ -507,23 +1111,27 @@ table tr.header { | |||
| 507 | } | 1111 | } |
| 508 | 1112 | ||
| 509 | #search_widget input { | 1113 | #search_widget input { |
| 510 | width: calc(100% - 70px); | 1114 | width: 100%; |
| 1115 | box-sizing: border-box; | ||
| 1116 | padding: 8px 12px; | ||
| 1117 | border-radius: 2px; | ||
| 511 | font-size: 18px; | 1118 | font-size: 18px; |
| 512 | } | 1119 | } |
| 513 | 1120 | ||
| 514 | #search_results p { | 1121 | .search_results p { |
| 515 | margin: 0; | 1122 | margin: 0; |
| 516 | padding: 4px 4px 0 4px; | 1123 | padding: 4px 4px 0 4px; |
| 517 | border-bottom: 1px solid #e8e8e8; | 1124 | border-bottom: 1px solid #e8e8e8; |
| 518 | } | 1125 | } |
| 519 | 1126 | ||
| 520 | #search_results p a { | 1127 | .search_results p a { |
| 1128 | text-decoration: none; | ||
| 521 | display: block; | 1129 | display: block; |
| 522 | font-weight: bold; | 1130 | font-weight: bold; |
| 523 | font-size: 0.95rem; | 1131 | font-size: 0.95rem; |
| 524 | } | 1132 | } |
| 525 | 1133 | ||
| 526 | #search_results p span.result_path { | 1134 | .search_results p span.result_path { |
| 527 | display: block; | 1135 | display: block; |
| 528 | margin-top: 2px; | 1136 | margin-top: 2px; |
| 529 | font-size: 0.75rem; | 1137 | font-size: 0.75rem; |
| @@ -532,7 +1140,7 @@ table tr.header { | |||
| 532 | padding-bottom: 4px; | 1140 | padding-bottom: 4px; |
| 533 | } | 1141 | } |
| 534 | 1142 | ||
| 535 | #search_results p.search_more { | 1143 | .search_results p.search_more { |
| 536 | margin: 0; | 1144 | margin: 0; |
| 537 | padding: 6px 4px; | 1145 | padding: 6px 4px; |
| 538 | font-size: 0.8rem; | 1146 | font-size: 0.8rem; |
| @@ -541,13 +1149,19 @@ table tr.header { | |||
| 541 | border-bottom: none; | 1149 | border-bottom: none; |
| 542 | } | 1150 | } |
| 543 | 1151 | ||
| 544 | table#content th.description { | 1152 | .search_results p.search_group_label { |
| 545 | width: 100px; | 1153 | margin: 8px 0 2px; |
| 1154 | padding: 0; | ||
| 1155 | border-bottom: none; | ||
| 1156 | font-size: 0.75rem; | ||
| 1157 | color: #969696; | ||
| 1158 | text-transform: lowercase; | ||
| 1159 | font-weight: normal; | ||
| 546 | } | 1160 | } |
| 547 | 1161 | ||
| 548 | table#content th.content { | 1162 | /* ============================================================ |
| 549 | width: 690px; | 1163 | Menu items (navigation editor) |
| 550 | } | 1164 | ============================================================ */ |
| 551 | 1165 | ||
| 552 | #menu_item_list { | 1166 | #menu_item_list { |
| 553 | border-collapse: collapse; | 1167 | border-collapse: collapse; |
| @@ -565,6 +1179,10 @@ table#content th.content { | |||
| 565 | height: 20px; | 1179 | height: 20px; |
| 566 | } | 1180 | } |
| 567 | 1181 | ||
| 1182 | #menu_item_list td.menu_item_title { | ||
| 1183 | width: 200px; | ||
| 1184 | } | ||
| 1185 | |||
| 568 | #menu_item_list td.menu_sort_handle div { | 1186 | #menu_item_list td.menu_sort_handle div { |
| 569 | background-color: #989898; | 1187 | background-color: #989898; |
| 570 | height: 26px; | 1188 | height: 26px; |
| @@ -583,14 +1201,14 @@ table#content th.content { | |||
| 583 | cursor: grab; | 1201 | cursor: grab; |
| 584 | } | 1202 | } |
| 585 | 1203 | ||
| 586 | #menu_item_list td.menu_item_title { | 1204 | .ui-state-highlight td { |
| 587 | width: 200px; | ||
| 588 | } | ||
| 589 | |||
| 590 | .ui-state-highlight td{ | ||
| 591 | height: 20px; | 1205 | height: 20px; |
| 592 | } | 1206 | } |
| 593 | 1207 | ||
| 1208 | /* ============================================================ | ||
| 1209 | Image box / browser | ||
| 1210 | ============================================================ */ | ||
| 1211 | |||
| 594 | #metadata ul#image_box { | 1212 | #metadata ul#image_box { |
| 595 | box-sizing: border-box; | 1213 | box-sizing: border-box; |
| 596 | margin: 0; | 1214 | margin: 0; |
| @@ -616,7 +1234,77 @@ div#image_browser ul li { | |||
| 616 | list-style-type: none; | 1234 | list-style-type: none; |
| 617 | } | 1235 | } |
| 618 | 1236 | ||
| 619 | table.user_list td.user_login { | 1237 | .thumbnail_list { |
| 620 | padding-right: 30px; | 1238 | display: flex; |
| 1239 | flex-wrap: wrap; | ||
| 1240 | gap: 0.75rem; | ||
| 1241 | list-style: none; | ||
| 1242 | margin: 0 0 0.75rem; | ||
| 1243 | padding: 0; | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | .thumbnail_list li { | ||
| 1247 | position: relative; | ||
| 1248 | display: flex; | ||
| 1249 | align-items: center; | ||
| 1250 | gap: 0.25rem; | ||
| 1251 | border: 1px solid #e8e8e8; | ||
| 1252 | border-radius: 6px; | ||
| 1253 | padding: 4px; | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | .thumbnail_list img { | ||
| 1257 | display: block; | ||
| 1258 | width: 60px; | ||
| 1259 | height: 60px; | ||
| 1260 | object-fit: cover; | ||
| 1261 | border-radius: 4px; | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | .related_asset_handle { | ||
| 1265 | display: flex; | ||
| 1266 | color: #969696; | ||
| 1267 | cursor: grab; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | .related_asset_handle svg { | ||
| 1271 | width: 1rem; | ||
| 1272 | height: 1rem; | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | .related_asset_remove { | ||
| 1276 | display: flex; | ||
| 1277 | align-items: center; | ||
| 1278 | justify-content: center; | ||
| 1279 | background: none; | ||
| 1280 | border: none; | ||
| 1281 | padding: 2px; | ||
| 1282 | cursor: pointer; | ||
| 1283 | color: #969696; | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | .related_asset_remove svg { | ||
| 1287 | width: 0.9rem; | ||
| 1288 | height: 0.9rem; | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | .related_asset_remove:hover { | ||
| 1292 | color: #cc0000; | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | .related_asset_result { | ||
| 1296 | display: flex; | ||
| 1297 | align-items: center; | ||
| 1298 | gap: 0.5rem; | ||
| 1299 | padding: 4px; | ||
| 1300 | border-bottom: 1px solid #e8e8e8; | ||
| 1301 | text-decoration: none; | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | .related_asset_result img { | ||
| 1305 | width: 40px; | ||
| 1306 | height: 40px; | ||
| 1307 | object-fit: cover; | ||
| 1308 | border-radius: 4px; | ||
| 621 | } | 1309 | } |
| 622 | 1310 | ||
diff --git a/public/stylesheets/ccc.css b/public/stylesheets/ccc.css index 30eb8dd..fe2144f 100644 --- a/public/stylesheets/ccc.css +++ b/public/stylesheets/ccc.css | |||
| @@ -216,7 +216,7 @@ div#frontpage_calendar { | |||
| 216 | } | 216 | } |
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | div#frontpage_calendar h2, div#tags h2, div#featured_articles h2, div.main_navigation h2 { | 219 | div#frontpage_calendar h2, div#tags h2, div#featured_articles h2, div#open_erfas_today h2, div.main_navigation h2 { |
| 220 | border-top: 2px solid; | 220 | border-top: 2px solid; |
| 221 | border-bottom: 2px solid; | 221 | border-bottom: 2px solid; |
| 222 | font-size: 1.1em; | 222 | font-size: 1.1em; |
| @@ -234,6 +234,18 @@ div#frontpage_calendar h2 { | |||
| 234 | margin-top: 0; | 234 | margin-top: 0; |
| 235 | } | 235 | } |
| 236 | 236 | ||
| 237 | div#open_erfas_today li { | ||
| 238 | margin-top: 8px; | ||
| 239 | margin-bottom: 8px; | ||
| 240 | line-height: 1.25; | ||
| 241 | } | ||
| 242 | |||
| 243 | div#open_erfas_today .event_time { | ||
| 244 | font-style: italic; | ||
| 245 | font-family: Georgia; | ||
| 246 | color: color-mix(in srgb, CanvasText, #808080); | ||
| 247 | } | ||
| 248 | |||
| 237 | @media(max-width:1016px) { | 249 | @media(max-width:1016px) { |
| 238 | div#tags li { | 250 | div#tags li { |
| 239 | list-style-type: none; | 251 | list-style-type: none; |
| @@ -248,7 +260,7 @@ div#frontpage_calendar h2 { | |||
| 248 | } | 260 | } |
| 249 | 261 | ||
| 250 | @media(min-width:1016px) { | 262 | @media(min-width:1016px) { |
| 251 | div#frontpage_calendar h2, div#tags h2, div#featured_articles h2 { | 263 | div#frontpage_calendar h2, div#tags h2, div#featured_articles h2, div#open_erfas_today h2 { |
| 252 | font-size: 1rem; | 264 | font-size: 1rem; |
| 253 | } | 265 | } |
| 254 | 266 | ||
| @@ -260,9 +272,9 @@ div#frontpage_calendar h2 { | |||
| 260 | 272 | ||
| 261 | } | 273 | } |
| 262 | 274 | ||
| 263 | div#frontpage_calendar ul, div#tags ul, div#featured_articles ul { | 275 | div#frontpage_calendar ul, div#tags ul, div#featured_articles ul, div#open_erfas_today ul { |
| 264 | padding: 0px; | 276 | padding: 0px; |
| 265 | font-size: 1rem; | 277 | font-size: 0.9rem; |
| 266 | line-height: 1.5em; | 278 | line-height: 1.5em; |
| 267 | } | 279 | } |
| 268 | 280 | ||
| @@ -278,11 +290,11 @@ div#frontpage_calendar li { | |||
| 278 | margin-bottom: 20px; | 290 | margin-bottom: 20px; |
| 279 | } | 291 | } |
| 280 | 292 | ||
| 281 | div#frontpage_calendar li, div#tags li, div#featured_articles li { | 293 | div#frontpage_calendar li, div#tags li, div#featured_articles li, div#open_erfas_today li { |
| 282 | list-style-type: none; | 294 | list-style-type: none; |
| 283 | } | 295 | } |
| 284 | 296 | ||
| 285 | div#frontpage_calendar li a, div#tags li a, div#featured_articles li a { | 297 | div#frontpage_calendar li a, div#tags li a, div#featured_articles li a, div#open_erfas_today li a { |
| 286 | text-decoration: none; | 298 | text-decoration: none; |
| 287 | color: color-mix(in srgb, CanvasText, #808080 25%); | 299 | color: color-mix(in srgb, CanvasText, #808080 25%); |
| 288 | } | 300 | } |
