123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044 |
- const app = Vue.createApp({
-
- delimiters: ['[[', ']]'],
-
- data() {
- return {
-
- rule: global.rule || {
- ruleID: "",
- timestamp: "",
- icon: "",
- name: "",
- lineage: "",
- summary: "",
- creator: {
- name: "",
- url: "",
- },
- modules: []
- },
- legacy: false,
- rID: false,
- loading: false,
- publishing: false,
- view: (global.rule) ? true : false,
- preview: (global.rule) ? true : false,
- template: (global.rule) ? true : false,
- steinAPI: 'https://api.steinhq.com/v1/storages/5e8b937ab88d3d04ae0816a5',
-
- history: [],
-
- editor: {
- source: null,
- previousState: null,
- module: null,
- },
-
- holding: false,
-
- icons: {
- plus: '/assets/tabler_icons/circle-plus.svg',
- info: '/assets/tabler_icons/info-circle.svg',
- chevron: '/assets/tabler_icons/chevrons-down.svg',
- blank: '/assets/tabler_icons/circle-dotted.svg',
- view: '/assets/tabler_icons/eye.svg',
- edit: '/assets/tabler_icons/tool.svg',
- plus: '/assets/tabler_icons/plus.svg',
- minus: '/assets/tabler_icons/minus.svg',
- publish: '/assets/tabler_icons/cloud-upload.svg',
- download: '/assets/tabler_icons/download.svg',
- export: '/assets/tabler_icons/file-download.svg',
- upload: '/assets/tabler_icons/file-upload.svg',
- fork: '/assets/tabler_icons/git-fork.svg',
- },
-
- moduleIcons: {
- culture: '/assets/tabler_icons/palette.svg',
- decision: '/assets/tabler_icons/thumb-up.svg',
- process: '/assets/tabler_icons/rotate.svg',
- structure: '/assets/tabler_icons/building.svg',
- relationship: '/assets/tabler_icons/heart.svg',
- economic: '/assets/tabler_icons/coin.svg',
- legal: '/assets/tabler_icons/license.svg',
- map: '/assets/tabler_icons/map.svg',
- communications: '/assets/tabler_icons/microphone.svg',
- },
-
- ruleIcons: {
- atom: '/assets/tabler_icons/atom.svg',
- bandage: '/assets/tabler_icons/bandage.svg',
- book: '/assets/tabler_icons/book.svg',
- box: '/assets/tabler_icons/box.svg',
- church: '/assets/tabler_icons/building-church.svg',
- store: '/assets/tabler_icons/building-store.svg',
- brush: '/assets/tabler_icons/brush.svg',
- car: '/assets/tabler_icons/car.svg',
- clock: '/assets/tabler_icons/clock.svg',
- cloud: '/assets/tabler_icons/cloud.svg',
- compass: '/assets/tabler_icons/compass.svg',
- game: '/assets/tabler_icons/device-gamepad.svg',
- flask: '/assets/tabler_icons/flask.svg',
- location: '/assets/tabler_icons/location.svg',
- moon: '/assets/tabler_icons/moon.svg',
- settings: '/assets/tabler_icons/settings.svg',
- shield: '/assets/tabler_icons/shield.svg',
- star: '/assets/tabler_icons/star.svg',
- tool: '/assets/tabler_icons/tool.svg',
- world: '/assets/tabler_icons/world.svg',
- },
-
- moduleTemplate: {
- moduleID: "",
- name: "",
- icon: "",
- summary: "",
- type: "",
- modules: []
- },
-
- moduleLibrary: 'culture',
- moduleTypes: {
-
-
-
-
-
- culture: {
- question: 'What are the core missions, values, and norms?',
- icon: '/assets/tabler_icons/palette.svg',
- open: true
- },
- decision: {
- question: 'Who can make decisions and how?',
- icon: '/assets/tabler_icons/thumb-up.svg',
- open: false
- },
- process: {
- question: 'How are policies implemented, and how do they evolve?',
- icon: '/assets/tabler_icons/rotate.svg ',
- open: false
- },
- structure: {
- question: 'What kinds of roles and internal entities are there?',
- icon: '/assets/tabler_icons/building.svg',
- open: false
- }
- },
-
-
- customModules: [],
-
- modules: global.modules,
- exports: {
- markdown: null,
- json: null,
- }
- }
- },
-
- provide() {
- return {
- editor: this.editor,
- icons: this.icons,
- }
- },
- created() {
- this.addToEditor(this.newModule());
- var urlParams = new URLSearchParams(window.location.search);
- if (urlParams.has('r')) {
- this.rID = urlParams.get('r');
- this.preview = true;
- this.view = true;
- this.fetchRule(this.rID);
- }
- },
- computed: {
-
- ruleExport() {
-
-
- function cleanModules(module) {
- const newModule = {
- moduleID: module.moduleID,
- name: module.name,
- icon: (module.icon && !module.icon.includes('http')) ? global.url + module.icon : module.icon,
- summary: module.summary,
- type: module.type,
- modules: (module.modules) ? module.modules.map(cleanModules) : [],
- }
- return newModule;
- }
- return {
- ruleID: (this.rule.ruleID) ? this.rule.ruleID : this.slugify(this.rule.name),
- timestamp: this.timesString(),
- icon: (this.rule.icon && !this.rule.icon.includes('http')) ? global.url + this.rule.icon : this.rule.icon,
- name: this.rule.name,
- lineage: this.rule.lineage,
- summary: this.rule.summary,
- creator: {
- name: this.rule.creator.name,
- url: this.rule.creator.url,
- },
- modules: this.rule.modules.map(cleanModules)
- }
- },
-
- json() {
- return JSON.stringify(this.ruleExport, null, 2);
- },
-
- listModuleIds() {
- const modules = [...this.rule.modules, ...this.customModules];
- return modules.map(module => module.moduleID)
- },
-
- moduleInEditor() {
- return this.moduleEditor[0]
- },
-
-
- editorHasEdits() {
- return this.editor.module && Object.entries(this.editor.module).toString() !== Object.entries(this.editor.previousState).toString();
- },
- },
- methods: {
-
-
- newModule() {
- return { ...this.moduleTemplate }
- },
-
- cloneModule(sourceModule,includeSubmodules) {
- let output = {
- ...this.moduleTemplate,
- ...sourceModule,
-
- source: sourceModule,
- };
- if (!includeSubmodules) output.modules = [];
-
- delete output.content;
- delete output.readonly;
-
-
- return output;
- },
-
- handleClickCopyModule(ev) {
- const clickTarget = this.getClosestModule(ev.target);
- if (!clickTarget) return;
- this.copyModule(clickTarget.module);
- },
-
- handleClickEditModule(ev) {
- const clickTarget = this.getClosestModule(ev.target);
- if (!clickTarget) return;
- this.editModule(clickTarget.module);
- },
-
- copyModule(module) {
- this.copyToEditor(module);
- },
-
- editModule(module) {
- this.addToEditor(module);
- },
-
- addModule(module,target = this.rule) {
- target.modules.push(module);
- },
-
- removeModule(module, target = this.rule) {
- if (! this.moduleContains(module, target)) return;
-
- target.modules.forEach((m, idx) => {
- if (m === module) {
- target.modules.splice(idx, 1);
- return;
- } else {
- this.removeModule(module, m);
- }
- });
- },
-
- deleteModule(module) {
- this.removeCustomModule(module);
-
- this.clearEditor();
- },
-
- startDragModule(ev) {
- const targetModule = this.getClosestModule(ev.target);
- if (!targetModule) return;
- const module = targetModule.module;
- ev.dataTransfer.setDragImage(targetModule, ev.offsetX, ev.offsetY);
-
- this.holding = {module};
- },
-
- rearrangeModule(ev) {
- const targetModule = this.getClosestModule(ev.target);
- if (!targetModule) return;
- const source = this.getClosestModule(targetModule.parentNode).module;
- const module = targetModule.module;
- ev.dataTransfer.setDragImage(targetModule, ev.offsetX, ev.offsetY);
- this.holding = {
- module,
- source,
- };
- },
-
- endDragModule() {
- this.holding = false;
- },
-
-
- moduleContains(needle, haystack = this.rule) {
- if (! haystack.modules ) return false;
- if (haystack.modules.includes(needle)) return true;
- return haystack.modules.some(submodule => this.moduleContains(needle, submodule));
- },
-
-
- dropOnRule(ev) {
-
- const landingNode = this.getClosestModule(ev.target);
- if (!this.holding.module || !landingNode) return;
- const landingModule = landingNode.module;
- const holdingModule = this.holding.module;
- if (holdingModule === landingModule) return;
-
- const readonly = holdingModule.readonly;
- const module = (readonly) ? this.cloneModule(holdingModule) : holdingModule;
- if (this.holding.source) {
-
- this.removeModule(holdingModule, this.holding.source);
- }
-
- this.addModule(module, landingModule);
- this.editModule(module);
- this.endDragModule();
- },
- fetchRule(id) {
- this.loading = true;
-
-
- let redirect = {
- 'benevolent_dictator': 'benevolent-dictator',
- circles: 'circles',
- consensus: 'consensus',
- 'do-ocracy': 'do-ocracy',
- 'elected_board': 'elected-board',
- jury: 'jury',
- petition: 'petition',
- 'self-appointed_board': 'self-appointed-board',
- }
-
- if (redirect[id]) {
- location.href = `/templates/${redirect[id]}`;
- return;
- }
- const store = new SteinStore(
- this.steinAPI
- );
- (async () => {
- var rule = [];
-
- await store.read('rules', { search: { ruleID: id } }).then(data => {
-
- if (data.length > 0) {
- rule = data[0];
- }
- console.log(rule);
- });
-
-
- if (!rule) {
- this.loading = false;
- return;
- }
-
- if (rule.version < 3) {
- this.loading = false;
- this.legacy = true;
- this.rule = rule;
- Vue.nextTick(() => {
- if (rule.version == 2) displayBuilderHTML();
- });
- return;
- }
- this.rule = {
- ruleID: rule.ruleID,
- timestamp: rule.timestamp,
- icon: rule.icon,
- name: rule.name,
- lineage: rule.lineage,
- summary: rule.summary,
- creator: {
- name: rule.creatorName,
- url: rule.creatorUrl,
- },
- modules: (rule.modules) ? JSON.parse(rule.modules) : []
- }
- this.loading = false;
-
- })();
- },
-
-
- addToEditor(module) {
- this.preventEditorLoss();
- this.setEditorSource(module);
- this.setEditorPreviousState(module);
- this.editor.module = module;
- },
-
- copyToEditor(module) {
- const moduleCopy = this.cloneModule(module);
- this.preventEditorLoss();
- this.setEditorSource(module);
- this.setEditorPreviousState(moduleCopy);
- this.editor.module = moduleCopy;
- },
-
- setEditorPreviousState(module) {
- this.editor.previousState = { ...module };
- },
-
- setEditorSource(module) {
- this.editor.source = module;
- },
-
- preventEditorLoss() {
-
- if (this.editorHasEdits && !this.moduleContains(this.editor.module)) {
- this.confirm('You have unsaved changes. Are you sure you want to discard them?')
- this.addCustomModule(this.editor.module);
- }
- },
-
- clickAddModule() {
- const module = this.editor.module;
- this.addModule(module);
- this.addToEditor(module);
- },
-
- clickRemoveModule() {
- const module = this.editor.module;
- this.removeModule(module);
- },
-
- clearEditor() {
- this.preventEditorLoss();
- this.editor.module = null;
- this.editor.previousState = null;
- },
-
- saveEditor() {
- this.addCustomModule(this.editor.module);
- this.setEditorPreviousState(this.editor.module);
- },
-
-
-
- addCustomModule(module) {
-
- if (!this.customModules.includes(module)) {
- this.customModules.unshift(module);
- }
- },
-
- newCustomModule() {
- const module = this.newModule();
- module.name = 'New Module';
- this.addToEditor(module);
- },
-
- removeCustomModule(module) {
- this.confirm("are you sure you want to remove this custom module?");
- const index = this.customModules.indexOf(module);
- this.customModules.splice(index, 1);
- },
-
-
- confirm(msg) {
- console.log(msg);
- },
-
-
- handleClickPublish() {
-
- if (!confirm("Publish to the public Library?")) return;
- if ( !this.rule.name ) {
- alert("Please enter a name for this rule.");
- return;
- }
- if ( this.rule.modules.length < 1 ) {
- alert("Please add at least one module to this rule.");
- return;
- }
- this.publishing = true;
- const rule = this.ruleExport;
- const ruleID = new Date().getTime();
-
- const store = new SteinStore(
- this.steinAPI
- );
- store.append('rules', [{
- ruleID: ruleID,
- timestamp: rule.timestamp,
- icon: rule.icon,
- name: rule.name,
- lineage: rule.lineage,
- summary: rule.summary,
- modules: this.jsonify(rule.modules),
- creatorName: rule.creator.name,
- creatorUrl: rule.creator.url,
- version: 3
- }]).then(data => {
- this.publishing = false;
- window.open("/create/?r=" + ruleID, "_self", false);
- });
- },
-
- handleClickDownload() {
- const yaml = jsyaml.dump(this.ruleExport);
- const turndown = new TurndownService();
- const html = document.getElementById('rule-export');
- if (!html) return;
- const markdown = turndown.turndown(html);
- const output = markdown + '\n\n----\n```yaml\n' + yaml + '\n```';
- this.saveFile(`${this.ruleExport.ruleID}.md`, output, 'text/markdown');
- },
-
- handleClickExport() {
- const output = this.json;
- this.saveFile(`${this.ruleExport.ruleID}.json`, output, 'application/json');
- },
-
- saveFile(filename, data, type) {
- const blob = new Blob([data], { type: type });
- if (window.navigator.msSaveOrOpenBlob) {
- window.navigator.msSaveBlob(blob, filename);
- }
- else {
- const elem = window.document.createElement('a');
- elem.href = window.URL.createObjectURL(blob);
- elem.download = filename;
- document.body.appendChild(elem);
- elem.click();
- document.body.removeChild(elem);
- }
- },
-
-
- getClosestModule(el) {
- const parent = el.closest('[data-module-id]');
- if (!parent) return false;
- if (!parent.module) return false;
- return parent;
- },
-
- clickPreview() {
- if(this.template) this.rule.icon = '';
- this.view = false;
- this.preview = !this.preview;
- },
-
- getModulesByType(type) {
- return this.modules.filter(module => module.type === type)
- },
-
- slugify(string) {
- const separator = '-';
- return string
- .toString()
- .normalize('NFD')
- .replace(/[\u0300-\u036f]/g, '')
- .toLowerCase()
- .replace(/[^a-z0-9 -]/g, '')
- .trim()
- .replace(/\s+/g, separator);
- },
-
- timesString(date) {
- let now = new Date(date);
- if (isNaN(now)) now = new Date();
- return now.getUTCFullYear() + '.' + (now.getUTCMonth() + 1) + '.' + now.getUTCDate()
- + ' ' + now.getUTCHours() + ":" + now.getUTCMinutes() + ":" + now.getUTCSeconds()
- + ' UTC';
- },
-
- jsonify(obj) {
- return JSON.stringify(obj, null, 2);
- },
-
- getUniqueId(test) {
- let id = test;
- let i = 0;
- while (this.listModuleIds.includes(id)) {
- i++;
- id = `${test}-${i}`;
- }
- return id
- },
- },
- });
- app.component('module', {
- delimiters: ['[[', ']]'],
- inject: ['editor'],
- props: {
- module: {
- type: Object,
- required: true,
- },
- inEditor: {
- type: Boolean,
- default: false,
- },
- hideSubmodules: {
- type: Boolean,
- default: false,
- }
- },
- data() {
- return {
- defaultIcon: '/assets/tabler_icons/circle-dotted.svg',
- mouseOver: false,
- dragOver: false
- }
- },
- computed: {
- icon() {
- return this.module.icon ? this.module.icon : this.defaultIcon;
- },
- moduleClass() {
- return {
- 'in-editor': this.editor.source == this.module,
- 'mouse-over': this.mouseOver,
-
- 'drag-over': this.dragOver
- }
- }
- },
- template: `
- <div
- class="module"
- :class="moduleClass"
- .module="module"
- @mouseover.stop="this.mouseOver = true"
- @mouseout="this.mouseOver = false"
- @dragenter.self="this.dragOver = true"
- @dragleave.self="this.dragOver = false"
- @drop.self="this.dragOver = false"
- :data-module-id="module.moduleID"
- >
- <div class="module__icon-wrapper">
- <span class="module__grain"><img src="/assets/tabler_icons/grain.svg"></span>
- <span class="module__icon"><img :src="icon"></span>
- </div>
- [[module.name]]
- <div class="submodules" v-if="!hideSubmodules && module.modules && module.modules.length">
- <module v-for="submodule in module.modules" :module="submodule" draggable="true"></module>
- </div>
- </div>
- `
- })
- app.component('moduleDisplay', {
- delimiters: ['[[', ']]'],
- props: {
- module: {
- type: Object,
- required: true,
- }
- },
- data() {
- return {
- defaultIcon: '/assets/tabler_icons/circle-dotted.svg'
- }
- },
- computed: {
- icon() {
- return this.module.icon ? this.module.icon : this.defaultIcon;
- }
- },
- template: `
- <div
- class="module"
- .module="module"
- >
- <div class="module__icon-wrapper">
- <span class="module__icon"><img :src="icon"></span>
- </div>
- [[module.name]]
- <div class="submodules" v-if="module.modules && module.modules.length">
- <module-display v-for="submodule in module.modules" :module="submodule"></module-display>
- </div>
- </div>
- `
- })
- app.component('moduleList', {
- delimiters: ['[[', ']]'],
- props: {
- module: {
- type: Object,
- required: true,
- },
- hideIcon: {
- type: Boolean,
- default: false,
- }
- },
- data() {
- return {
- defaultIcon: '/assets/tabler_icons/circle-dotted.svg'
- }
- },
- computed: {
- icon() {
- return this.module.icon ? this.module.icon : this.defaultIcon;
- }
- },
- template: `
- <li class="module-list-item">
- <span class="module__icon" v-if="!hideIcon"><img :src="icon"> </span><strong>[[module.name]]</strong>: [[module.summary]]
- <ul class="submodules" v-if="module.modules && module.modules.length">
- <module-list v-for="submodule in module.modules" :module="submodule" :hide-icon="hideIcon"></module-list>
- </ul>
- </li>
- `
- })
- app.component('vueButton', {
- delimiters: ['[[', ']]'],
- props: {
- icon: {
- type: String,
- required: false,
- default: false
- },
- loading: {
- type: Boolean,
- required: false,
- default: false
- }
- },
- computed: {
- classList() {
- return {
- 'has-icon': this.icon,
- 'is-loading': this.loading
- };
- },
- activeIcon() {
- return this.loading ? '/assets/tabler_icons/refresh.svg' : this.icon;
- }
- },
- template: `
- <button class="btn" :class="classList"><img :src="activeIcon" v-if="icon"> <slot>Click Here</slot></button>
- `
- })
- app.component('icon', {
- delimiters: ['[[', ']]'],
- props: {
- icon: {
- type: String,
- required: true,
- default: '/assets/tabler_icons/circle-dotted.svg'
- }
- },
- template: `
- <span class="icon"><img :src="icon"></span>
- `
- })
- vm = app.mount('#app')
- function builderArray() {
- var modules = document.getElementById("module-input").children;
-
-
- function iterateArray(childs) {
- var moduleArray = [];
- if (childs.length > 0) {
- for (var i = 0; i < childs.length; i++) {
- module = childs[i];
- if (module.classList[0] == "module") {
- var moduleName = module.children.item("module-name");
- var moduleData = moduleName.title;
- var moduleChilds = module.children;
- moduleArray.push(
- [stripHTML(moduleName.innerHTML),
- stripHTML(moduleData),
- iterateArray(moduleChilds)]);
- }
- }
- }
- return moduleArray;
- }
- return iterateArray(modules);
- }
- function displayBuilderHTML() {
- var output = "";
- var mainArray = builderArray();
- function arrayHTML(thisArray) {
- var thisOutput = "";
- if (thisArray.length > 0) {
- thisOutput += '<ul>\n';
- for (var i = 0; i < thisArray.length; i++) {
- var item = thisArray[i];
- thisOutput += '<li><strong>' + item[0] + '</strong> ';
- thisOutput += item[1] + '</li>\n';
- if (item[2].length > 0) {
- thisOutput += arrayHTML(item[2]);
- }
- }
- thisOutput += '</ul>\n';
- }
- return thisOutput
- }
- if (mainArray.length > 0) {
- output += '<div class="builder-list">\n';
- output += arrayHTML(mainArray) + '\n</div>\n';
- }
- document.getElementById("builder-field").innerHTML = output;
- }
- function stripHTML(input) {
- input = input.replace(/<br ?\/?>/ig, "\n").replace(/(<([^>]+)>)/ig, '');
- return input;
- }
|