diff --git a/js/interface/toolbars.js b/js/interface/toolbars.js index 3ebcc127..0bc25f58 100644 --- a/js/interface/toolbars.js +++ b/js/interface/toolbars.js @@ -80,17 +80,22 @@ export class Toolbar { } } } + /** + * Builds the toolbar from data + * @param {object} data Data used to build the toolbar + * @param {boolean} force If true, customization data will be ignored. Used when resetting toolbar + */ build(data, force) { var scope = this; //Items this.children.length = 0; var items = data.children - if (!force && BARS.stored[scope.id] && typeof BARS.stored[scope.id] === 'object') { - items = BARS.stored[scope.id] + if (!force && BARS.stored[this.id] && typeof BARS.stored[this.id] === 'object') { + items = BARS.stored[this.id]; if (data.children) { - // Add new actions to existing toolbars + // Add new actions (newly added via bb update) to existing toolbars data.children.forEach((key, index) => { - if (typeof key == 'string' && key.length > 1 && !items.includes(key) && !Keybinds.stored[key] && BarItems[key]) { + if (typeof key == 'string' && key.length > 1 && !items.includes(key) && !Keybinds.stored[key] && BARS.stored._known?.includes(key) == false && BarItems[key]) { // Figure out best index based on item before. Otherwise use index from original array let prev_index = items.indexOf(data.children[index-1]); if (prev_index != -1) index = prev_index+1; @@ -100,7 +105,7 @@ export class Toolbar { } } if (items && items instanceof Array) { - var content = $(scope.node).find('div.content') + var content = $(this.node).find('div.content') content.children().detach() for (var itemPosition = 0; itemPosition < items.length; itemPosition++) { let item = items[itemPosition]; @@ -112,7 +117,10 @@ export class Toolbar { continue; } - if (typeof item == 'string') item = BarItems[item] + if (typeof item == 'string') { + BARS.stored._known?.safePush(item); + item = BarItems[item]; + } if (item) { item.pushToolbar(this); @@ -127,8 +135,8 @@ export class Toolbar { } } } - $(scope.node).toggleClass('no_wrap', this.no_wrap) - $(scope.node).toggleClass('vertical', this.vertical) + $(this.node).toggleClass('no_wrap', this.no_wrap) + $(this.node).toggleClass('vertical', this.vertical) if (data.default_place) { this.toPlace(this.id) } @@ -294,7 +302,10 @@ export class Toolbar { } }) BARS.stored[this.id] = arr; - if (arr.equals(this.default_children)) { + let identical_to_default = this.default_children.length == arr.length && this.default_children.allAre((item, i) => { + return arr[i] == item || (typeof arr[i] == 'string' && arr[i].startsWith(item)); + }) + if (identical_to_default) { delete BARS.stored[this.id]; } // Temporary fix @@ -325,7 +336,9 @@ Toolbar.prototype.menu = new Menu([ ]) export const BARS = { - stored: {}, + stored: { + _known: [] + }, editing_bar: undefined, action_definers: [], condition: Condition, @@ -687,6 +700,9 @@ export const BARS = { stored = JSON.parse(stored) if (typeof stored === 'object') { BARS.stored = stored; + if (!BARS.stored._known) { + BARS.stored._known = []; + } } } @@ -808,9 +824,6 @@ export const BARS = { } }) } - Blockbench.onUpdateTo('4.4.0-beta.0', () => { - delete BARS.stored.brush; - }) Toolbars.brush = new Toolbar({ id: 'brush', no_wrap: true, diff --git a/js/io/formats/bbmodel.js b/js/io/formats/bbmodel.js index c81485c5..fb43202d 100644 --- a/js/io/formats/bbmodel.js +++ b/js/io/formats/bbmodel.js @@ -262,7 +262,7 @@ var codec = new Codec('project', { if (Animation.all.length) { model.animations = []; Animation.all.forEach(a => { - model.animations.push(a.getUndoCopy({bone_names: true, absolute_paths: options.absolute_paths}, true)) + model.animations.push(a.getUndoCopy({absolute_paths: options.absolute_paths}, true)) }) } if (AnimationController.all.length) { diff --git a/js/io/io.js b/js/io/io.js index 5c3d7610..e7fd3e6d 100644 --- a/js/io/io.js +++ b/js/io/io.js @@ -487,149 +487,6 @@ export const Extruder = { Undo.finishEdit('Add extruded texture', {elements: selected, outliner: true, textures: [Texture.all[Texture.all.length-1]]}) } } -//Json -export function compileJSON(object, options = {}) { - let indentation = options.indentation; - if (typeof indentation !== 'string') { - switch (settings.json_indentation.value) { - case 'spaces_4': indentation = ' '; break; - case 'spaces_2': indentation = ' '; break; - case 'tabs': default: indentation = '\t'; break; - } - } - function newLine(tabs) { - if (options.small === true) {return '';} - let s = '\n'; - for (let i = 0; i < tabs; i++) { - s += indentation; - } - return s; - } - function escape(string) { - return string.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n|\r\n/g, '\\n').replace(/\t/g, '\\t') - } - function handleVar(o, tabs, breaks = true) { - var out = '' - if (typeof o === 'string') { - //String - out += '"' + escape(o) + '"' - } else if (typeof o === 'boolean') { - //Boolean - out += (o ? 'true' : 'false') - } else if (o === null || o === Infinity || o === -Infinity) { - //Null - out += 'null' - } else if (typeof o === 'number') { - //Number - o = (Math.round(o*100000)/100000).toString() - out += o - } else if (o instanceof Array) { - //Array - let has_content = false - let multiline = !!o.find(item => typeof item === 'object'); - if (!multiline) { - let length = 0; - o.forEach(item => { - length += typeof item === 'string' ? (item.length+4) : 3; - }); - if (length > 140) multiline = true; - } - out += '[' - for (var i = 0; i < o.length; i++) { - var compiled = handleVar(o[i], tabs+1) - if (compiled) { - if (has_content) {out += ',' + ((options.small || multiline) ? '' : ' ')} - if (multiline) {out += newLine(tabs)} - out += compiled - has_content = true - } - } - if (multiline) {out += newLine(tabs-1)} - out += ']' - } else if (typeof o === 'object') { - //Object - breaks = breaks && o.constructor.name !== 'oneLiner'; - var has_content = false - out += '{' - for (var key in o) { - if (o.hasOwnProperty(key)) { - var compiled = handleVar(o[key], tabs+1, breaks) - if (compiled) { - if (has_content) {out += ',' + (breaks || options.small?'':' ')} - if (breaks) {out += newLine(tabs)} - out += '"' + escape(key) + '":' + (options.small === true ? '' : ' ') - out += compiled - has_content = true - } - } - } - if (breaks && has_content) {out += newLine(tabs-1)} - out += '}' - } - return out; - } - let file = handleVar(object, 1); - if ((settings.final_newline.value && options.final_newline != false) || options.final_newline == true) { - file += '\n'; - } - return file; -} -export function autoParseJSON(data, feedback) { - if (data.substr(0, 4) === '') { - data = LZUTF8.decompress(data.substr(4), {inputEncoding: 'StorageBinaryString'}) - } - if (data.charCodeAt(0) === 0xFEFF) { - data = data.substr(1) - } - try { - data = JSON.parse(data) - } catch (err1) { - data = data.replace(/\/\*[^(\*\/)]*\*\/|\/\/.*/g, '') - try { - data = JSON.parse(data) - } catch (err) { - if (feedback === false) return; - if (data.match(/\n\r?[><]{7}/)) { - Blockbench.showMessageBox({ - title: 'message.invalid_file.title', - icon: 'fab.fa-git-alt', - message: 'message.invalid_file.merge_conflict' - }) - return; - } - let error_part = ''; - function logErrantPart(whole, start, length) { - var line = whole.substr(0, start).match(/\n/gm) - line = line ? line.length+1 : 1 - var result = ''; - var lines = whole.substr(start, length).split(/\n/gm) - lines.forEach((s, i) => { - result += `#${line+i} ${s}\n` - }) - error_part = result.substr(0, result.length-1) + ' <-- HERE'; - console.log(error_part); - } - console.error(err) - var length = err.toString().split('at position ')[1] - if (length) { - length = parseInt(length) - var start = limitNumber(length-32, 0, Infinity) - - logErrantPart(data, start, 1+length-start) - } else if (err.toString().includes('Unexpected end of JSON input')) { - - logErrantPart(data, data.length-16, 10) - } - Blockbench.showMessageBox({ - translateKey: 'invalid_file', - icon: 'error', - message: tl('message.invalid_file.message', [err]) + (error_part ? `\n\n\`\`\`\n${error_part}\n\`\`\`` : '') - }) - return; - } - } - return data; -} BARS.defineActions(function() { diff --git a/js/main.js b/js/main.js index c9277541..056290e3 100644 --- a/js/main.js +++ b/js/main.js @@ -20,6 +20,7 @@ import "./preview/OrbitControls" import './languages' import "./util/util" +import "./util/json" import "./util/three_custom" import "./util/math_util" import "./util/array_util" diff --git a/js/util/json.js b/js/util/json.js new file mode 100644 index 00000000..f04317fa --- /dev/null +++ b/js/util/json.js @@ -0,0 +1,161 @@ +export function compileJSON(object, options = {}) { + let indentation = options.indentation; + if (typeof indentation !== 'string') { + switch (settings.json_indentation.value) { + case 'spaces_4': indentation = ' '; break; + case 'spaces_2': indentation = ' '; break; + case 'tabs': default: indentation = '\t'; break; + } + } + function newLine(tabs) { + if (options.small === true) {return '';} + let s = '\n'; + for (let i = 0; i < tabs; i++) { + s += indentation; + } + return s; + } + function escape(string) { + if (string.includes('\\')) { + string = string.replace(/\\/g, '\\\\'); + } + if (string.includes('"')) { + string = string.replace(/"/g, '\\"'); + } + if (string.includes('\n')) { + string = string.replace(/\n|\r\n/g, '\\n'); + } + if (string.includes('\t')) { + string = string.replace(/\t/g, '\\t'); + } + return string; + } + function handleVar(o, tabs, breaks = true) { + var out = '' + let type = typeof o; + if (type === 'string') { + //String + out += '"' + escape(o) + '"' + } else if (type === 'boolean') { + //Boolean + out += (o ? 'true' : 'false') + } else if (o === null || o === Infinity || o === -Infinity) { + //Null + out += 'null' + } else if (type === 'number') { + //Number + o = (Math.round(o*100000)/100000).toString() + if (o == 'NaN') o = null + out += o + } else if (o instanceof Array) { + //Array + let has_content = false + let multiline = !!o.find(item => typeof item === 'object'); + if (!multiline) { + let length = 0; + o.forEach(item => { + length += typeof item === 'string' ? (item.length+4) : 3; + }); + if (length > 140) multiline = true; + } + out += '[' + for (var i = 0; i < o.length; i++) { + var compiled = handleVar(o[i], tabs+1) + if (compiled) { + if (has_content) {out += ',' + ((options.small || multiline) ? '' : ' ')} + if (multiline) {out += newLine(tabs)} + out += compiled + has_content = true + } + } + if (multiline) {out += newLine(tabs-1)} + out += ']' + } else if (type === 'object') { + //Object + breaks = breaks && o.constructor.name !== 'oneLiner'; + var has_content = false + out += '{' + for (var key in o) { + if (o.hasOwnProperty(key)) { + var compiled = handleVar(o[key], tabs+1, breaks) + if (compiled) { + if (has_content) {out += ',' + (breaks || options.small?'':' ')} + if (breaks) {out += newLine(tabs)} + out += '"' + escape(key) + '":' + (options.small === true ? '' : ' ') + out += compiled + has_content = true + } + } + } + if (breaks && has_content) {out += newLine(tabs-1)} + out += '}' + } + return out; + } + let file = handleVar(object, 1); + if ((settings.final_newline.value && options.final_newline != false) || options.final_newline == true) { + file += '\n'; + } + return file; +} +export function autoParseJSON(data, feedback) { + if (data.substr(0, 4) === '') { + data = LZUTF8.decompress(data.substr(4), {inputEncoding: 'StorageBinaryString'}) + } + if (data.charCodeAt(0) === 0xFEFF) { + data = data.substr(1) + } + try { + data = JSON.parse(data) + } catch (err1) { + data = data.replace(/\/\*[^(\*\/)]*\*\/|\/\/.*/g, '') + try { + data = JSON.parse(data) + } catch (err) { + if (feedback === false) return; + if (data.match(/\n\r?[><]{7}/)) { + Blockbench.showMessageBox({ + title: 'message.invalid_file.title', + icon: 'fab.fa-git-alt', + message: 'message.invalid_file.merge_conflict' + }) + return; + } + let error_part = ''; + function logErrantPart(whole, start, length) { + var line = whole.substr(0, start).match(/\n/gm) + line = line ? line.length+1 : 1 + var result = ''; + var lines = whole.substr(start, length).split(/\n/gm) + lines.forEach((s, i) => { + result += `#${line+i} ${s}\n` + }) + error_part = result.substr(0, result.length-1) + ' <-- HERE'; + console.log(error_part); + } + console.error(err) + var length = err.toString().split('at position ')[1] + if (length) { + length = parseInt(length) + var start = limitNumber(length-32, 0, Infinity) + + logErrantPart(data, start, 1+length-start) + } else if (err.toString().includes('Unexpected end of JSON input')) { + + logErrantPart(data, data.length-16, 10) + } + Blockbench.showMessageBox({ + translateKey: 'invalid_file', + icon: 'error', + message: tl('message.invalid_file.message', [err]) + (error_part ? `\n\n\`\`\`\n${error_part}\n\`\`\`` : '') + }) + return; + } + } + return data; +} + +Object.assign(window, { + compileJSON, + autoParseJSON, +})