1/** 2 * Adapted for modules AdvancedResourceTemplate and AdvancedSearch. 3 * Contains fixes pulled on github. 4 */ 5 6var Omeka = { 7 openSidebar : function(sidebar) { 8 sidebar.addClass('active'); 9 this.reserveSidebarSpace(); 10 sidebar.trigger('o:sidebar-opened'); 11 if ($('.active.sidebar').length > 1) { 12 var highestIndex = 3; // The CSS currently defines the default sidebar z-index as 3. 13 $('.active.sidebar').each(function() { 14 var currentIndex = parseInt($(this).css('zIndex'), 10); 15 if (currentIndex > highestIndex) { 16 highestIndex = currentIndex; 17 } 18 }); 19 sidebar.css('zIndex', highestIndex + 1); 20 } 21 }, 22 23 closeSidebar : function(sidebar) { 24 sidebar.removeClass('active'); 25 this.reserveSidebarSpace(); 26 sidebar.trigger('o:sidebar-closed'); 27 }, 28 29 reserveSidebarSpace: function() { 30 var openSidebars = $('.active.sidebar, .always-open.sidebar').length 31 - $('.section:not(.active) .active.sidebar, .section:not(.active) .always-open.sidebar').length; 32 $('body').toggleClass('sidebar-open', openSidebars > 0); 33 }, 34 35 populateSidebarContent : function(sidebar, url, data) { 36 var sidebarContent = sidebar.find('.sidebar-content'); 37 sidebar.addClass('loading'); 38 sidebarContent.empty(); 39 $.get(url, data) 40 .done(function(data) { 41 sidebarContent.html(data); 42 $(sidebar).trigger('o:sidebar-content-loaded'); 43 }) 44 .fail(function() { 45 sidebarContent.html('<p>' + Omeka.jsTranslate('Something went wrong') + '</p>'); 46 }) 47 .always(function () { 48 sidebar.removeClass('loading'); 49 }); 50 }, 51 52 switchActiveSection: function (section) { 53 var closedSection = $('.section.active'); 54 var sectionId = '#' + section.attr('id'); 55 $('.section.active, .section-nav li.active').removeClass('active'); 56 section.addClass('active'); 57 $('.section-nav a[href="' + sectionId + '"]').parent().addClass('active'); 58 if (!$('body').hasClass('no-section-hashes')) { 59 history.replaceState(null, document.title, sectionId); 60 } 61 this.reserveSidebarSpace(); 62 if (!closedSection.is(section)) { 63 if (closedSection.length > 0) { 64 closedSection.trigger('o:section-closed'); 65 } 66 section.trigger('o:section-opened'); 67 } 68 }, 69 70 filterSelector : function() { 71 var filter = $(this).val().toLowerCase(); 72 var selector = $(this).closest('.selector'); 73 var totalCount = 0; 74 selector.find('li.selector-parent').each(function() { 75 var parent = $(this); 76 var count = 0; 77 parent.find('li.selector-child').each(function() { 78 var child = $(this); 79 var label = child.data('child-search').toLowerCase(); 80 if ((label.indexOf(filter) < 0) || (child.hasClass('added'))) { 81 // Label doesn't contain the filter string. Hide the child. 82 child.addClass('filter-hidden'); 83 } else { 84 // Label contains the filter string. Show the child. 85 child.removeClass('filter-hidden'); 86 totalCount++; 87 count++; 88 } 89 }); 90 if (count > 0) { 91 parent.removeClass('empty'); 92 } else { 93 parent.addClass('empty'); 94 } 95 parent.children('span.selector-child-count').text(count); 96 }); 97 if (filter == '') { 98 selector.find('li.selector-parent').removeClass('show'); 99 $('.filter-match').removeClass('filter-match'); 100 } 101 selector.find('span.selector-total-count').text(totalCount); 102 }, 103 104 updateSearch: function () { 105 var checkedOption = $("#advanced-options input[type='radio']:checked "); 106 $("#search-form").attr("action", checkedOption.data('action')); 107 $("#search-form > input[type='text']").attr("placeholder", checkedOption.data('inputPlaceholder')).attr("aria-label", checkedOption.data('inputPlaceholder')); 108 }, 109 110 scrollTo: function(wrapper) { 111 if (wrapper.length) { 112 $('html, body').animate({ 113 scrollTop: (wrapper.offset().top -100) 114 },200); 115 } 116 }, 117 118 markDirty: function(form) { 119 $(form).data('omekaFormDirty', true); 120 }, 121 122 fixIframeAspect: function () { 123 $('iframe').each(function () { 124 var aspect = $(this).attr('height') / $(this).attr('width'); 125 $(this).height($(this).width() * aspect); 126 }); 127 }, 128 129 framerateCallback: function(callback) { 130 var waiting = false;
131 callback = callback.bind(this); 132 return function () { 133 if (!waiting) { 134 waiting = true; 135 window.requestAnimationFrame(function () { 136 callback(); 137 waiting = false; 138 }); 139 } 140 } 141 }, 142 143 warnIfUnsaved: function() { 144 var setSubmittedFlag = function () { 145 $(this).data('omekaFormSubmitted', true); 146 }; 147 148 var setOriginalData = function () { 149 $(this).data('omekaFormOriginalData', $(this).serialize()); 150 }; 151 152 var formsToCheck = $('form[method=POST]:not(.disable-unsaved-warning)'); 153 formsToCheck.on('o:form-loaded', setOriginalData); 154 formsToCheck.each(function () { 155 var form = $(this); 156 form.trigger('o:form-loaded'); 157 form.submit(setSubmittedFlag); 158 }); 159 160 $(window).on('beforeunload', function() { 161 var preventNav = false; 162 formsToCheck.each(function () { 163 var form = $(this); 164 var originalData = form.data('omekaFormOriginalData'); 165 var hasFile = false; 166 if (form.data('omekaFormSubmitted')) { 167 return; 168 } 169 170 form.trigger('o:before-form-unload'); 171 172 form.find('input[type=file]').each(function () { 173 if (this.files.length) { 174 hasFile = true; 175 return false; 176 } 177 }); 178 179 if (form.data('omekaFormDirty') 180 || (originalData && originalData !== form.serialize()) 181 || hasFile 182 ) { 183 preventNav = true; 184 return false; 185 } 186 }); 187 188 if (preventNav) { 189 return 'You have unsaved changes.'; 190 } 191 }); 192 }, 193 194 manageSelectedActions: function() { 195 var selectedOptions = $('#batch-form .batch-inputs [value$=selected]'); 196 if ($('.batch-edit td input[type="checkbox"]:checked').length > 0) { 197 selectedOptions.removeAttr('disabled'); 198 } else { 199 selectedOptions.attr('disabled', true); 200 $('.batch-actions-select').val('default'); 201 $('.batch-actions .active').removeClass('active'); 202 $('.batch-actions .default').addClass('active'); 203 } 204 }, 205 206 initializeSelector : function(tableId, selectorId) { 207 var table = $(tableId); 208 var existingRowData = table.data('existing-rows') || []; 209 var rowTemplate = $($.parseHTML(table.data('rowTemplate'))); 210 var selector = $(selectorId); 211 var totalCount = selector.find('.resources-available').data('all-resources-count'); 212 var selectorCount = selector.find('.selector-total-count'); 213 214 var parentToggle = function(e) { 215 e.stopPropagation(); 216 if ($(this).children('li')) { 217 $(this).toggleClass('show'); 218 } 219 } 220 221 var appendRow = function(id) { 222 if (table.find(".resource-id[value='" + id + "']").length) { 223 return; 224 } 225 var tableRow = rowTemplate.clone(); 226 var selectorRow = selector.find('[data-resource-id="' + id + '"]'); 227 tableRow.find('.resource-id').val(id); 228 tableRow.find('.data-value').each(function() { 229 var tableRowCell = $(this); 230 var tableRowKey = tableRowCell.data('row-key'); 231 var tableRowValue = selectorRow.data(tableRowKey); 232 tableRowCell.text(tableRowValue); 233 }); 234 selectorRow.addClass('added'); 235 table.append(tableRow).removeClass('empty').trigger('appendRow'); 236 updateResourceCount(id); 237 } 238 239 var updateResourceCount = function(id) { 240 var resource = selector.find('[data-resource-id="' + id + '"]'); 241 var resourceParent = resource.parents('.selector-parent'); 242 var childCount = resourceParent.find('.selector-child-count').first(); 243 if (resource.hasClass('added')) { 244 var newTotalCount = parseInt(selectorCount.text()) - 1; 245 var newChildCount = parseInt(childCount.text()) - 1; 246 } else { 247 var newTotalCount = parseInt(selectorCount.text()) + 1; 248 var newChildCount = parseInt(childCount.text()) + 1; 249 } 250 selectorCount.text(newTotalCount); 251 childCount.text(newChildCount); 252 var currentRows = table.find('.resource-row').length; 253 if (totalCount - currentRows == 0) { 254 selector.find('.resources-available').addClass('empty'); 255 } else { 256 selector.find('.resources-available').removeClass('empty'); 257 } 258 if (newChildCount == 0) { 259 resourceParent.addClass('empty'); 260 } else { 261 resourceParent.removeClass('empty'); 262 } 263 } 264 265 if (existingRowData.length > 0) { 266 $.each(existingRowData, function() { 267 appendRow(this.id); 268 }); 269 table.removeClass('empty'); 270 } 271 272 // Add the selected resource to the edit panel. 273 $(selectorId + ' .selector-child').on('click', function(e) { 274 e.stopPropagation(); 275 var selectorRow = $(this); 276 var selectorParent = selectorRow.parents('.selector-parent'); 277 selectorParent.unbind('click'); 278 appendRow(selectorRow.data('resource-id')); 279 selectorParent.bind('click', parentToggle); 280 Omeka.scrollTo(table.find('.resource-row:last-child')); 281 }); 282 283 // Remove a resource from the edit panel. 284 table.on('click', '.o-icon-delete', function(e) { 285 e.preventDefault(); 286 var row = $(this).closest('.resource-row'); 287 var resourceId = row.find('.resource-id').val(); 288 selector.find('[data-resource-id="' + resourceId + '"]').removeClass('added'); 289 row.remove(); 290 updateResourceCount(resourceId); 291 if ($('.resource-row').length < 1) { 292 table.addClass('empty'); 293 } 294 }); 295 }, 296 297 298 // @see http://stackoverflow.com/questions/7035825/regular-expression-for-a-language-tag-as-defined-by-bcp47 299 // Removes `|[A-Za-z]{4}|[A-Za-z]{5,8}` from the "language" portion because, 300 // while in the spec, it does not represent current usage. 301 langIsValid: function(lang) { 302 return lang.match(/^(((en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|n
302o-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?))(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+))$/); 303 }, 304 305 // Index of property search values. 306 propertySearchIndex: null, 307 308 // Prepare the search form. Must be called any time the form is loaded. 309 prepareSearchForm: function(form) { 310 // The property values need an index. 311 Omeka.propertySearchIndex = $('#property-queries .value').length; 312 // Prepare the multi-value templates used for duplicating values. 313 $('.multi-value.field').each(function() { 314 var field = $(this); 315 var value = field.find('.value').first().clone(); 316 var valueHtml = value.wrap('<div></div>').parent().html(); 317 field.data('field-template', valueHtml); 318 }); 319 form.find('.query-type').each(Omeka.disableQueryTextInput); 320 }, 321 322 // Disable query text according to query type. 323 disableQueryTextInput: function() { 324 var queryType = $(this); 325 var queryText = queryType.siblings('.query-text'); 326 queryText.prop('disabled', ['ex', 'nex', 'exs', 'nexs', 'exm', 'nexm', 'lex', 'nlex', 'tpl', 'ntpl', 'tpr', 'ntpr', 'tpu', 'ntpu'].includes(queryType.val())); 327 }, 328 329 // Clean the search query of empty or otherwise unneeded inputs. 330 cleanSearchQuery: function(form) { 331 form.find(':input').each(function(index) { 332 const input = $(this); 333 const inputName = input.attr('name'); 334 const inputValue = input.val(); 335 if (inputName && '' === inputValue) { 336 const inputNames = [ 337 'fulltext_search', 338 'resource_class_id[]', 339 'resource_template_id[]', 340 'item_set_id[]', 341 'site_id', 342 'owner_id', 343 'media_type', 344 'sort_by', 345 'sort_order', 346 'is_public', 347 'has_media', 348 'id', 349 ]; 350 if (inputNames.includes(inputName)) { 351 input.prop('name', ''); 352 } else { 353 const match = inputName.match(/property\[(\d+)\]\[text\]/); 354 if (match) { 355 const propertyType = form.find(`[name="property[${match[1]}][type]"]`); 356 if (['eq', 'neq', 'in', 'nin', 'res', 'nres', 'list', 'nlist', 'sw', 'nsw', 'ew', 'new', 'near', 'nnear', 'lres', 'nlres', 'tp', 'ntp', 'dtp', 'ndtp', 'gt', 'gte', 'lte', 'lt'] 357 .includes(propertyType.val()) 358 ) { 359 form.find(`[name="property[${match[1]}][joiner]"]`).prop('name', ''); 360 form.find(`[name="property[${match[1]}][property]"]`).prop('name', ''); 361 form.find(`[name="property[${match[1]}][text]"]`).prop('name', ''); 362 propertyType.prop('name', ''); 363 } 364 } 365 } 366 } 367 }); 368 }, 369 370 closeOpenPageActionsMenu: function(e) { 371 var button = $('.page-action-menu .collapse'); 372 if (button.length > 0) { 373 button.click(); 374 } 375 } 376}; 377 378$(document).ready(function() { 379 // Set classes for expandable/collapsible content. 380 $(document).on('click', 'a.expand, a.collapse', function(e) { 381 e.preventDefault(); 382 var toggle = $(this); 383 toggle.toggleClass('collapse').toggleClass('expand'); 384 if (toggle.hasClass('expand')) { 385 toggle.attr('aria-label', Omeka.jsTranslate('Expand')).attr('title', Omeka.jsTranslate('Expand')); 386 toggle.trigger('o:collapsed'); 387 } else { 388 toggle.attr('aria-label', Omeka.jsTranslate('Collapse')).attr('title', Omeka.jsTranslate('Collapse')); 389 toggle.trigger('o:expanded'); 390 } 391 }); 392});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.