+
+ ns.save_file = function(base64_data, content_type, size, attachment_name) {
+ // atob returns a unicode string with one codepoint per octet. revert this
+ const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
+ const byteCharacters = atob(b64Data);
+ const byteArrays = [];
+
+ for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
+ const slice = byteCharacters.slice(offset, offset + sliceSize);
+
+ const byteNumbers = new Array(slice.length);
+ for (let i = 0; i < slice.length; i++) {
+ byteNumbers[i] = slice.charCodeAt(i);
+ }
+
+ const byteArray = new Uint8Array(byteNumbers);
+ byteArrays.push(byteArray);
+ }
+
+ const blob = new Blob(byteArrays, {type: contentType});
+ return blob;
+ }
+
+ var blob = b64toBlob(base64_data, content_type);
+ var a = $("<a style='display: none;'/>");
+ var url = window.URL.createObjectURL(blob);
+ a.attr("href", url);
+ a.attr("download", attachment_name);
+ $("body").append(a);
+ a[0].click();
+ window.URL.revokeObjectURL(url);
+ a.remove();
+ }
+
+ ns.detect_duplicate_ids_in_dom = function() {
+ var ids = {},
+ found = false;
+
+ $('[id]').each(function() {
+ if (this.id && ids[this.id]) {
+ found = true;
+ console.warn('Duplicate ID #' + this.id);
+ }
+ ids[this.id] = 1;
+ });
+
+ if (!found)
+ console.log('No duplicate IDs found :)');
+ };
+
+ ns.validate_form = function(selector) {
+ if (!kivi.Validator) {
+ console.log('kivi.Validator is not loaded');
+ } else {
+ return kivi.Validator.validate_all(selector);
+ }
+ };
+
+ // Verifies that at least one checkbox matching the
+ // "checkbox_selector" is actually checked. If not, an error message
+ // is shown, and false is returned. Otherwise (at least one of them
+ // is checked) nothing is shown and true returned.
+ //
+ // Can be used in checks when clicking buttons.
+ ns.check_if_entries_selected = function(checkbox_selector) {
+ if ($(checkbox_selector + ':checked').length > 0)
+ return true;
+
+ alert(kivi.t8('No entries have been selected.'));
+
+ return false;
+ };
+
+ ns.switch_areainput_to_textarea = function(id) {
+ var $input = $('#' + id);
+ if (!$input.length)
+ return;
+
+ var $area = $('<textarea></textarea>');
+
+ $area.prop('rows', 3);
+ $area.prop('cols', $input.prop('size') || 40);
+ $area.prop('name', $input.prop('name'));
+ $area.prop('id', $input.prop('id'));
+ $area.val($input.val());
+
+ $input.parent().replaceWith($area);
+ $area.focus();
+ };
+
+ ns.set_cursor_position = function(selector, position) {
+ var $input = $(selector);
+ if (position === 'end')
+ position = $input.val().length;
+
+ $input.prop('selectionStart', position);
+ $input.prop('selectionEnd', position);
+ };
+
+ ns._shell_escape = function(str) {
+ if (str.match(/^[a-zA-Z0-9.,_=+/-]+$/))
+ return str;
+
+ return "'" + str.replace(/'/, "'\\''") + "'";
+ };
+
+ ns.call_as_curl = function(params) {
+ params = params || {};
+ var uri = document.documentURI.replace(/\?.*/, '');
+ var command = ['curl', '--user', kivi.myconfig.login + ':SECRET', '--request', params.method || 'POST']
+
+ $(params.data || []).each(function(idx, elt) {
+ command = command.concat([ '--form-string', elt.name + '=' + (elt.value || '') ]);
+ });
+
+ command.push(uri);
+
+ return $.map(command, function(elt, idx) {
+ return kivi._shell_escape(elt);
+ }).join(' ');
+ };
+
+ ns.serialize = function(source, target = [], prefix, in_array = false) {
+ let arr_prefix = first => in_array ? (first ? "[+]" : "[]") : "";
+
+ if (Array.isArray(source) ) {
+ source.forEach(( val, i ) => {
+ ns.serialize(val, target, prefix + arr_prefix(i == 0), true);
+ });
+ } else if (typeof source === "object") {
+ let first = true;
+ for (let key in source) {
+ ns.serialize(source[key], target, (prefix !== undefined ? prefix + arr_prefix(first) + "." : "") + key);
+ first = false;
+ }
+ } else {
+ target.push({ name: prefix + arr_prefix(false), value: source });
+ }
+
+ return target;
+ };