Feature update

-- Added text basic text formatting toolbar as floating widget for selected text

-- Implement unique content sanitization and upload
This commit is contained in:
sairaj mote 2021-11-10 04:04:09 +05:30
parent f161bd8c43
commit 7adda3e71f
7 changed files with 707 additions and 340 deletions

View File

@ -2257,6 +2257,7 @@ smSelect.innerHTML = `
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 700;
}
.selection{
border-radius: 0.3rem;
@ -2356,6 +2357,7 @@ customElements.define('sm-select', class extends HTMLElement {
this.availableOptions
this.previousOption
this.isOpen = false;
this.label = ''
this.slideDown = [{
transform: `translateY(-0.5rem)`,
opacity: 0
@ -2386,7 +2388,7 @@ customElements.define('sm-select', class extends HTMLElement {
this.selectedOptionText = this.shadowRoot.querySelector('.selected-option-text')
}
static get observedAttributes() {
return ['value', 'disabled']
return ['disabled', 'label']
}
get value() {
return this.getAttribute('value')
@ -2403,7 +2405,7 @@ customElements.define('sm-select', class extends HTMLElement {
}
firstElement.classList.add('check-selected')
this.value = firstElement.getAttribute('value')
this.selectedOptionText.textContent = firstElement.textContent
this.selectedOptionText.textContent = `${this.label}${firstElement.textContent}`
this.previousOption = firstElement;
if (fire) {
this.fireEvent()
@ -2464,7 +2466,7 @@ customElements.define('sm-select', class extends HTMLElement {
handleOptionSelection(e) {
if (this.previousOption !== document.activeElement) {
this.value = document.activeElement.getAttribute('value')
this.selectedOptionText.textContent = document.activeElement.textContent;
this.selectedOptionText.textContent = `${this.label}${document.activeElement.textContent}`;
this.fireEvent()
if (this.previousOption) {
this.previousOption.classList.remove('check-selected')
@ -2534,6 +2536,8 @@ customElements.define('sm-select', class extends HTMLElement {
} else {
this.selection.setAttribute('tabindex', '0')
}
} else if (name === 'label') {
this.label = this.hasAttribute('label') ? `${this.getAttribute('label')} ` : ''
}
}
})
@ -3190,4 +3194,237 @@ customElements.define('sm-tab-panels', class extends HTMLElement {
intersectionObserver.disconnect()
document.removeEventListener(`switchedtab${this.id}`, this.handleTabChange)
}
})
const smSwitch = document.createElement('template')
smSwitch.innerHTML = `
<style>
*{
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 0;
margin: 0;
}
:host{
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
--accent-color: #4d2588;
--text-color: 17, 17, 17;
--background-color: 255, 255, 255;
}
label{
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
width: 100%;
outline: none;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
:host(:not([disabled])) label:focus-visible{
-webkit-box-shadow: 0 0 0 0.1rem var(--accent-color);
box-shadow: 0 0 0 0.1rem var(--accent-color);
}
:host([disabled]) {
cursor: not-allowed;
opacity: 0.6;
pointer-events: none;
}
.switch {
position: relative;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
width: 2.4rem;
flex-shrink: 0;
margin-left: auto;
padding: 0.2rem;
cursor: pointer;
border-radius: 2rem;
}
input {
display: none;
}
.track {
position: absolute;
left: 0;
right: 0;
height: 1.4rem;
-webkit-transition: background 0.3s;
-o-transition: background 0.3s;
transition: background 0.3s;
background: rgba(var(--text-color), 0.4);
-webkit-box-shadow: 0 0.1rem 0.3rem #00000040 inset;
box-shadow: 0 0.1rem 0.3rem #00000040 inset;
border-radius: 1rem;
}
.switch:active .button::after,
.switch:focus .button::after{
opacity: 1
}
.switch:focus-visible .button::after{
opacity: 1
}
.button::after{
content: '';
display: -webkit-box;
display: -ms-flexbox;
display: flex;
position: absolute;
height: 2.6rem;
width: 2.6rem;
background: rgba(var(--text-color), 0.2);
border-radius: 2rem;
opacity: 0;
-webkit-transition: opacity 0.3s;
-o-transition: opacity 0.3s;
transition: opacity 0.3s;
}
.button {
position: relative;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
height: 1rem;
width: 1rem;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
border-radius: 1rem;
-webkit-box-shadow: 0 0.1rem 0.4rem #00000060;
box-shadow: 0 0.1rem 0.4rem #00000060;
-webkit-transition: -webkit-transform 0.3s;
transition: -webkit-transform 0.3s;
-o-transition: transform 0.3s;
transition: transform 0.3s;
transition: transform 0.3s, -webkit-transform 0.3s;
border: solid 0.3rem white;
}
input:checked ~ .button {
-webkit-transform: translateX(100%);
-ms-transform: translateX(100%);
transform: translateX(100%);
}
input:checked ~ .track {
background: var(--accent-color);
}
</style>
<label tabindex="0">
<slot name="left"></slot>
<div part="switch" class="switch">
<input type="checkbox">
<div class="track"></div>
<div class="button"></div>
</div>
<slot name="right"></slot>
</label>`
customElements.define('sm-switch', class extends HTMLElement {
constructor() {
super()
this.attachShadow({
mode: 'open'
}).append(smSwitch.content.cloneNode(true))
this.switch = this.shadowRoot.querySelector('.switch');
this.input = this.shadowRoot.querySelector('input')
this.isChecked = false
this.isDisabled = false
this.dispatch = this.dispatch.bind(this)
}
static get observedAttributes() {
return ['disabled', 'checked']
}
get disabled() {
return this.isDisabled
}
set disabled(val) {
if (val) {
this.setAttribute('disabled', '')
} else {
this.removeAttribute('disabled')
}
}
get checked() {
return this.isChecked
}
set checked(value) {
if (value) {
this.setAttribute('checked', '')
} else {
this.removeAttribute('checked')
}
}
dispatch() {
this.dispatchEvent(new CustomEvent('change', {
bubbles: true,
composed: true,
detail: {
value: this.isChecked
}
}))
}
connectedCallback() {
this.addEventListener('keydown', e => {
if (e.code === "Space" && !this.isDisabled) {
e.preventDefault()
this.input.click()
}
})
this.input.addEventListener('click', e => {
if (this.input.checked)
this.checked = true
else
this.checked = false
this.dispatch()
})
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
if (name === 'disabled') {
if (this.hasAttribute('disabled')) {
this.disabled = true
}
else {
this.disabled = false
}
}
else if (name === 'checked') {
if (this.hasAttribute('checked')) {
this.isChecked = true
this.input.checked = true
}
else {
this.isChecked = false
this.input.checked = false
}
}
}
}
})

View File

@ -137,7 +137,6 @@ a:any-link:focus-visible {
sm-input {
font-size: 0.9rem;
--border-radius: 0.3rem;
--background: var(--accent-color--light);
}
sm-button {
@ -385,7 +384,7 @@ ul {
.icon-button {
padding: 0.6rem;
border-radius: 0.8rem;
background-color: var(--accent-color--light);
background-color: rgba(var(--text-color), 0.1);
height: -webkit-max-content;
height: -moz-max-content;
height: max-content;
@ -463,7 +462,7 @@ button:active,
align-items: center;
width: 100%;
grid-template-columns: auto 1fr;
gap: 0 0.3rem;
gap: 0 0.5rem;
margin-right: 1rem;
}
.logo h4 {
@ -499,26 +498,6 @@ details[open] > summary .icon {
transform: rotate(180deg);
}
strip-select {
--gap: 0;
background-color: var(--accent-color--light);
border-radius: 0.3rem;
}
strip-option {
font-weight: 500;
font-size: 0.8rem;
--border-radius: 0;
--active-option-color: rgba(var(--background-color), 1);
--active-option-background-color: var(--accent-color);
}
strip-option:first-of-type {
--border-radius: 0.3rem 0 0 0.3rem;
}
strip-option:last-of-type {
--border-radius: 0 0.3rem 0.3rem 0;
}
sm-select,
sm-option {
font-size: 0.9rem;
@ -553,15 +532,13 @@ sm-checkbox {
#main_header {
display: grid;
gap: 1rem;
padding: 1rem;
padding: 1rem 1.5rem;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
grid-template-columns: auto 1fr auto auto;
grid-column: 1/-1;
background-color: var(--foreground-color);
border-radius: 0 0 1rem 1rem;
border: solid thin rgba(var(--text-color), 0.16);
}
.label {
@ -607,18 +584,17 @@ sm-checkbox {
}
.content-card {
width: min(70ch, 100%);
width: min(60ch, 100%);
border-radius: 0.5rem;
background-color: var(--foreground-color);
border: solid thin rgba(var(--text-color), 0.16);
}
.content__area {
border-radius: inherit;
padding: 1rem;
line-height: 1.7;
white-space: pre-line;
font-size: 0.9rem !important;
font-size: 0.9rem;
line-height: 1.7;
color: rgba(var(--text-color), 0.8);
background-color: rgba(var(--text-color), 0.02);
border-radius: 0.5rem;
@ -626,6 +602,12 @@ sm-checkbox {
transition: -webkit-box-shadow 0.1s;
transition: box-shadow 0.1s;
transition: box-shadow 0.1s, -webkit-box-shadow 0.1s;
min-height: 70vh;
}
.content__area:empty::before {
content: attr(placeholder);
opacity: 0.6;
pointer-events: none;
}
.content__area:focus-within {
outline: none;
@ -653,6 +635,11 @@ sm-checkbox {
.formatting-button {
padding: 0.2rem;
border-radius: 0.3rem;
-webkit-transition: background-color 0.1s;
transition: background-color 0.1s;
}
.formatting-button.active:hover {
background-color: var(--accent-color);
}
.active {
@ -670,22 +657,45 @@ sm-checkbox {
-webkit-box-shadow: 0 0.5rem 1rem -0.5rem rgba(0, 0, 0, 0.1);
box-shadow: 0 0.5rem 1rem -0.5rem rgba(0, 0, 0, 0.1);
border-radius: 0.5rem;
overflow: hidden;
justify-self: center;
padding-left: 1.3rem;
}
.quote-template .icon {
fill: var(--accent-color);
height: 4rem;
width: 4rem;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
.quote-template::before {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
position: absolute;
content: "";
height: 100%;
width: 0.3rem;
background-color: var(--accent-color);
}
.quote-template blockquote {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.quote-template figcaption {
margin-top: 0.5rem;
color: rgba(var(--text-color), 0.8);
font-size: 0.8rem;
}
.quote-template figcaption::before {
content: "- ";
#text_toolbar {
position: absolute;
z-index: 1;
left: 0;
top: 0;
-webkit-transition: -webkit-transform 0.3s;
transition: -webkit-transform 0.3s;
transition: transform 0.3s;
transition: transform 0.3s, -webkit-transform 0.3s;
background-color: var(--foreground-color);
padding: 0.3rem;
border-radius: 0.3rem;
-webkit-box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.06), 0 0.5rem 1rem -0.5rem rgba(0, 0, 0, 0.1);
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.06), 0 0.5rem 1rem -0.5rem rgba(0, 0, 0, 0.1);
}
@media screen and (max-width: 40rem) and (any-hover: none) {
@ -772,7 +782,7 @@ button {
}
.interact:hover,
button:hover {
background-color: var(--accent-color--light);
background-color: rgba(var(--text-color), 0.1);
}
.transaction-card button {

2
css/main.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -118,7 +118,6 @@ a:any-link:focus-visible {
sm-input {
font-size: 0.9rem;
--border-radius: 0.3rem;
--background: var(--accent-color--light);
}
sm-button {
--padding: 0.7rem 1rem;
@ -348,7 +347,7 @@ ul {
.icon-button {
padding: 0.6rem;
border-radius: 0.8rem;
background-color: var(--accent-color--light);
background-color: rgba(var(--text-color), 0.1);
height: max-content;
.icon {
fill: var(--accent-color);
@ -409,7 +408,7 @@ button:active,
align-items: center;
width: 100%;
grid-template-columns: auto 1fr;
gap: 0 0.3rem;
gap: 0 0.5rem;
margin-right: 1rem;
h4 {
@ -440,24 +439,6 @@ details {
}
}
}
strip-select {
--gap: 0;
background-color: var(--accent-color--light);
border-radius: 0.3rem;
}
strip-option {
font-weight: 500;
font-size: 0.8rem;
--border-radius: 0;
--active-option-color: rgba(var(--background-color), 1);
--active-option-background-color: var(--accent-color);
&:first-of-type {
--border-radius: 0.3rem 0 0 0.3rem;
}
&:last-of-type {
--border-radius: 0 0.3rem 0.3rem 0;
}
}
sm-select,
sm-option {
font-size: 0.9rem;
@ -488,13 +469,11 @@ sm-checkbox {
#main_header {
display: grid;
gap: 1rem;
padding: 1rem;
padding: 1rem 1.5rem;
align-items: center;
grid-template-columns: auto 1fr auto auto;
grid-column: 1/-1;
background-color: var(--foreground-color);
border-radius: 0 0 1rem 1rem;
border: solid thin rgba(var(--text-color), 0.16);
}
.label {
@ -534,21 +513,29 @@ sm-checkbox {
}
.content-card {
width: min(70ch, 100%);
width: min(60ch, 100%);
border-radius: 0.5rem;
background-color: var(--foreground-color);
border: solid thin rgba(var(--text-color), 0.16);
&--empty {
}
}
.content__area {
border-radius: inherit;
padding: 1rem;
line-height: 1.7;
white-space: pre-line;
font-size: 0.9rem !important;
font-size: 0.9rem;
line-height: 1.7;
color: rgba(var(--text-color), 0.8);
background-color: rgba(var(--text-color), 0.02);
border-radius: 0.5rem;
transition: box-shadow 0.1s;
min-height: 70vh;
&:empty::before {
content: attr(placeholder);
opacity: 0.6;
pointer-events: none;
}
&:focus-within {
outline: none;
box-shadow: 0 0 0 0.1rem var(--accent-color) inset;
@ -573,6 +560,10 @@ sm-checkbox {
.formatting-button {
padding: 0.2rem;
border-radius: 0.3rem;
transition: background-color 0.1s;
&.active:hover {
background-color: var(--accent-color);
}
}
.active {
background-color: var(--accent-color);
@ -588,12 +579,19 @@ sm-checkbox {
background-color: var(--foreground-color);
box-shadow: 0 0.5rem 1rem -0.5rem rgba(0, 0, 0, 0.1);
border-radius: 0.5rem;
overflow: hidden;
justify-self: center;
.icon {
fill: var(--accent-color);
height: 4rem;
width: 4rem;
transform: scaleX(-1);
padding-left: 1.3rem;
&::before {
display: flex;
position: absolute;
content: "";
height: 100%;
width: 0.3rem;
background-color: var(--accent-color);
}
blockquote {
display: flex;
}
.quote {
}
@ -601,12 +599,22 @@ sm-checkbox {
margin-top: 0.5rem;
color: rgba(var(--text-color), 0.8);
font-size: 0.8rem;
&::before {
content: "- ";
}
}
}
#text_toolbar {
position: absolute;
z-index: 1;
left: 0;
top: 0;
transition: transform 0.3s;
background-color: var(--foreground-color);
padding: 0.3rem;
border-radius: 0.3rem;
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.06),
0 0.5rem 1rem -0.5rem rgba(0, 0, 0, 0.1);
}
@media screen and (max-width: 40rem) and (any-hover: none) {
.cancel-order {
span {
@ -686,7 +694,7 @@ sm-checkbox {
button {
transition: background-color 0.3s, transform 0.3s;
&:hover {
background-color: var(--accent-color--light);
background-color: rgba(var(--text-color), 0.1);
}
}
.transaction-card {

View File

@ -35,9 +35,7 @@
//for cloud apps
subAdmins: [],
application: "TEST_MODE",
appObjects: {
cc: {}
},
appObjects: {},
generalData: {},
lastVC: {}
}
@ -60,10 +58,12 @@
</sm-popup>
<header id="main_header">
<div class="logo">
<svg class="main-logo" viewBox="0 0 27.25 32">
<title>RanchiMall</title>
<path
d="M27.14,30.86c-.74-2.48-3-4.36-8.25-6.94a20,20,0,0,1-4.2-2.49,6,6,0,0,1-1.25-1.67,4,4,0,0,1,0-2.26c.37-1.08.79-1.57,3.89-4.55a11.66,11.66,0,0,0,3.34-4.67,6.54,6.54,0,0,0,.05-2.82C20,3.6,18.58,2,16.16.49c-.89-.56-1.29-.64-1.3-.24a3,3,0,0,1-.3.72l-.3.55L13.42.94C13,.62,12.4.26,12.19.15c-.4-.2-.73-.18-.72.05a9.39,9.39,0,0,1-.61,1.33s-.14,0-.27-.13C8.76.09,8-.27,8,.23A11.73,11.73,0,0,1,6.76,2.6C4.81,5.87,2.83,7.49.77,7.49c-.89,0-.88,0-.61,1,.22.85.33.92,1.09.69A5.29,5.29,0,0,0,3,8.33c.23-.17.45-.29.49-.26a2,2,0,0,1,.22.63A1.31,1.31,0,0,0,4,9.34a5.62,5.62,0,0,0,2.27-.87L7,8l.13.55c.19.74.32.82,1,.65a7.06,7.06,0,0,0,3.46-2.47l.6-.71-.06.64c-.17,1.63-1.3,3.42-3.39,5.42L6.73,14c-3.21,3.06-3,5.59.6,8a46.77,46.77,0,0,0,4.6,2.41c.28.13,1,.52,1.59.87,3.31,2,4.95,3.92,4.95,5.93a2.49,2.49,0,0,0,.07.77h0c.09.09,0,.1.9-.14a2.61,2.61,0,0,0,.83-.32,3.69,3.69,0,0,0-.55-1.83A11.14,11.14,0,0,0,17,26.81a35.7,35.7,0,0,0-5.1-2.91C9.37,22.64,8.38,22,7.52,21.17a3.53,3.53,0,0,1-1.18-2.48c0-1.38.71-2.58,2.5-4.23,2.84-2.6,3.92-3.91,4.67-5.65a3.64,3.64,0,0,0,.42-2A3.37,3.37,0,0,0,13.61,5l-.32-.74.29-.48c.17-.27.37-.63.46-.8l.15-.3.44.64a5.92,5.92,0,0,1,1,2.81,5.86,5.86,0,0,1-.42,1.94c0,.12-.12.3-.15.4a9.49,9.49,0,0,1-.67,1.1,28,28,0,0,1-4,4.29C8.62,15.49,8.05,16.44,8,17.78a3.28,3.28,0,0,0,1.11,2.76c.95,1,2.07,1.74,5.25,3.32,3.64,1.82,5.22,2.9,6.41,4.38A4.78,4.78,0,0,1,21.94,31a3.21,3.21,0,0,0,.14.92,1.06,1.06,0,0,0,.43-.05l.83-.22.46-.12-.06-.46c-.21-1.53-1.62-3.25-3.94-4.8a37.57,37.57,0,0,0-5.22-2.82A13.36,13.36,0,0,1,11,21.19a3.36,3.36,0,0,1-.8-4.19c.41-.85.83-1.31,3.77-4.15,2.39-2.31,3.43-4.13,3.43-6a5.85,5.85,0,0,0-2.08-4.29c-.23-.21-.44-.43-.65-.65A2.5,2.5,0,0,1,15.27.69a10.6,10.6,0,0,1,2.91,2.78A4.16,4.16,0,0,1,19,6.16a4.91,4.91,0,0,1-.87,3c-.71,1.22-1.26,1.82-4.27,4.67a9.47,9.47,0,0,0-2.07,2.6,2.76,2.76,0,0,0-.33,1.54,2.76,2.76,0,0,0,.29,1.47c.57,1.21,2.23,2.55,4.65,3.73a32.41,32.41,0,0,1,5.82,3.24c2.16,1.6,3.2,3.16,3.2,4.8a1.94,1.94,0,0,0,.09.76,4.54,4.54,0,0,0,1.66-.4C27.29,31.42,27.29,31.37,27.14,30.86ZM6.1,7h0a3.77,3.77,0,0,1-1.46.45L4,7.51l.68-.83a25.09,25.09,0,0,0,3-4.82A12,12,0,0,1,8.28.76c.11-.12.77.32,1.53,1l.63.58-.57.84A10.34,10.34,0,0,1,6.1,7Zm5.71-1.78A9.77,9.77,0,0,1,9.24,7.18h0a5.25,5.25,0,0,1-1.17.28l-.58,0,.65-.78a21.29,21.29,0,0,0,2.1-3.12c.22-.41.42-.76.44-.79s.5.43.9,1.24L12,5ZM13.41,3a2.84,2.84,0,0,1-.45.64,11,11,0,0,1-.9-.91l-.84-.9.19-.45c.34-.79.39-.8,1-.31A9.4,9.4,0,0,1,13.8,2.33q-.18.34-.39.69Z" />
<svg class="main-logo" width="23" height="22" viewBox="0 0 23 22" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M15 0.749881C10.9026 2.35002 8 6.33604 8 11C8 15.664 10.9026 19.65 15 21.2501C13.7603 21.7343 12.4112 22 11 22C4.92487 22 0 17.0751 0 11C0 4.92487 4.92487 0 11 0C12.4112 0 13.7603 0.26573 15 0.749881Z" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M14 16.591C16.2349 15.7182 17.8182 13.544 17.8182 11C17.8182 8.45602 16.2349 6.28183 14 5.40903C14.6762 5.14494 15.4121 5 16.1818 5C19.4955 5 22.1818 7.68629 22.1818 11C22.1818 14.3137 19.4955 17 16.1818 17C15.4121 17 14.6762 16.8551 14 16.591Z" />
</svg>
<div class="grid">
<span class="label">RanchiMall</span>
@ -71,8 +71,15 @@
</div>
</div>
<div id="article_name_wrapper" class="flex align-center justify-center">
<button title="Create new article" class="button__icon--left" onclick="showPopup('create_article_popup')">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
</svg>
</button>
<h4 id="current_article_name"></h4>
<button class="button__icon--right">
<button class="button__icon--right" onclick="showPopup('article_list_popup')">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M24 24H0V0h24v24z" fill="none" opacity=".87" />
@ -90,8 +97,8 @@
</svg>
</button>
</header>
<section>
<button id="strong_button" class="formatting-button" onclick="make.bold()">
<div id="text_toolbar" class="hide-completely">
<button id="strong_button" class="formatting-button" onclick="formatDoc('bold')">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
@ -99,14 +106,14 @@
d="M15.6 10.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H7v14h7.04c2.09 0 3.71-1.7 3.71-3.79 0-1.52-.86-2.82-2.15-3.42zM10 6.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3v-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z" />
</svg>
</button>
<button id="em_button" class="formatting-button" onclick="make.italic()">
<button id="em_button" class="formatting-button" onclick="formatDoc('italic');">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4h-8z" />
</svg>
</button>
<button id="u_button" class="formatting-button" onclick="make.underlined()">
<button id="u_button" class="formatting-button" onclick="formatDoc('underline');">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
@ -114,21 +121,8 @@
d="M12 17c3.31 0 6-2.69 6-6V3h-2.5v8c0 1.93-1.57 3.5-3.5 3.5S8.5 12.93 8.5 11V3H6v8c0 3.31 2.69 6 6 6zm-7 2v2h14v-2H5z" />
</svg>
</button>
<button id="a_button" class="formatting-button" onclick="make.link()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px"
viewBox="0 0 24 24" width="24px" fill="#000000">
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<path
d="M8,11h8v2H8V11z M20.1,12H22c0-2.76-2.24-5-5-5h-4v1.9h4C18.71,8.9,20.1,10.29,20.1,12z M3.9,12c0-1.71,1.39-3.1,3.1-3.1h4 V7H7c-2.76,0-5,2.24-5,5s2.24,5,5,5h4v-1.9H7C5.29,15.1,3.9,13.71,3.9,12z M19,12h-2v3h-3v2h3v3h2v-3h3v-2h-3V12z" />
</g>
</svg>
</button>
</section>
</div>
<div id="article_wrapper" class="grid page-layout"></div>
<div contenteditable="true"></div>
<section class="grid page-layout">
<div id="action_button_group" class="flex align-center gap-0-5">
<span>
@ -159,9 +153,64 @@
</button>
</div>
</section>
<sm-popup id="article_list_popup">
<header slot="header" class="popup__header">
<div class="grid">
<div class="flex align-center space-between">
<h3>All articles</h3>
<sm-select label="Sort by:">
<sm-option value="time">Date created</sm-option>
<sm-option value="az">A-Z</sm-option>
</sm-select>
</div>
<sm-input placeholder="Search articles" type="search"></sm-input>
</div>
</header>
</sm-popup>
<sm-popup id="create_article_popup">
<header slot="header" class="popup__header">
<button class="popup__header__close" onclick="hidePopup()">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
</svg>
</button>
<h3>Create article</h3>
</header>
<sm-form>
<sm-input placeholder="Article title" required></sm-input>
<sm-checkbox checked>
<div class="grid button__icon--right gap-0-5">
Set as default
<p style="font-size: 0.8rem;">
This article will be opened by default for everyone when CC is first loaded.
</p>
</div>
</sm-checkbox>
<!-- <sm-switch>
<div slot="left">
Make private
</div>
</sm-switch> -->
<sm-button variant="primary">Create</sm-button>
</sm-form>
</sm-popup>
<template id="section_template">
<h4 class="heading"></h4>
<section class="article-section flex gap-1">
<div class="content-card content-card--empty">
<div class="content__area" data-type="origin" placeholder="Write something new or edit existing content"
contenteditable="true"></div>
<button class="submit-entry">Submit</button>
</div>
<div class="content-card-container"></div>
</section>
</template>
<template id="content_card_template">
<div class="content-card">
<div class="paragraph-wrapper"></div>
<div class="content__area"></div>
<div class="content__options grid align-center">
<button class="version-history-button" title="See version history">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
@ -184,17 +233,12 @@
</div>
</template>
<template id="quote_template">
<figure class="quote-template grid gap-0-5">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
fill="#000000">
<path d="M0 0h24v24H0V0z" fill="none" />
<path
d="M7.17 17c.51 0 .98-.29 1.2-.74l1.42-2.84c.14-.28.21-.58.21-.89V8c0-.55-.45-1-1-1H5c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h2l-1.03 2.06c-.45.89.2 1.94 1.2 1.94zm10 0c.51 0 .98-.29 1.2-.74l1.42-2.84c.14-.28.21-.58.21-.89V8c0-.55-.45-1-1-1h-4c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h2l-1.03 2.06c-.45.89.2 1.94 1.2 1.94z" />
</svg>
<blockquote>
<p class="quote"></p>
<figure class="quote-template grid gap-0-5" contenteditable="false">
<blockquote class="flex">
<p class="quote" contenteditable="true"></p>
</blockquote>
<figcaption><span class="by"></span>, <cite class="citation"></cite></figcaption>
<figcaption class="flex"><span class="by" contenteditable="true"></span><cite class="citation"
contenteditable="true"></cite></figcaption>
</figure>
</template>
<script id="ui_utils">
@ -593,216 +637,263 @@
}
}
</script>
<script>
/* PatienceDiff
* https://github.com/jonTrent/PatienceDiff/blob/5411f78d0cf2a0d2277548ef3cc9626ccdeb4277/PatienceDiff.js
*/
window.patienceDiff = function (e, n, t) { function i(e, n, t) { var i = new Map; for (let l = n; l <= t; l++) { let n = e[l]; i.has(n) ? (i.get(n).count++, i.get(n).index = l) : i.set(n, { count: 1, index: l }) } return i.forEach((e, n, t) => { 1 !== e.count ? t.delete(n) : t.set(n, e.index) }), i } function l(e, n, t, l, o, d) { let r = i(e, n, t), u = i(l, o, d); return r.forEach((e, n, t) => { u.has(n) ? t.set(n, { indexA: e, indexB: u.get(n) }) : t.delete(n) }), r } let o = [], d = 0, r = 0, u = [], h = [], f = [], x = []; function s(t, i) { i < 0 ? (u.push(e[t]), h.push(o.length), d++) : t < 0 && (f.push(n[i]), x.push(o.length), r++), o.push({ line: 0 <= t ? e[t] : n[i], aIndex: t, bIndex: i }) } function g(t, i, o, d) { for (; t <= i && o <= d && e[t] === n[o];)s(t++, o++); let r = i; for (; t <= i && o <= d && e[i] === n[d];)i--, d--; let u = l(e, t, i, n, o, d); if (0 === u.size) { for (; t <= i;)s(t++, -1); for (; o <= d;)s(-1, o++) } else a(t, i, o, d, u); for (; i < r;)s(++i, ++d) } function a(t, i, o, d, r) { var u = function (e) { var n = []; e.forEach((e, t, i) => { let l = 0; for (; n[l] && n[l][n[l].length - 1].indexB < e.indexB;)l++; n[l] || (n[l] = []), 0 < l && (e.prev = n[l - 1][n[l - 1].length - 1]), n[l].push(e) }); var t = []; if (0 < n.length) { let e = n.length - 1; for (t = [n[e][n[e].length - 1]]; t[t.length - 1].prev;)t.push(t[t.length - 1].prev) } return t.reverse() }(r || l(e, t, i, n, o, d)); if (0 === u.length) g(t, i, o, d); else { let e; for ((t < u[0].indexA || o < u[0].indexB) && g(t, u[0].indexA - 1, o, u[0].indexB - 1), e = 0; e < u.length - 1; e++)g(u[e].indexA, u[e + 1].indexA - 1, u[e].indexB, u[e + 1].indexB - 1); (u[e].indexA <= i || u[e].indexB <= d) && g(u[e].indexA, i, u[e].indexB, d) } } return a(0, e.length - 1, 0, n.length - 1), t ? { lines: o, lineCountDeleted: d, lineCountInserted: r, lineCountMoved: 0, aMove: u, aMoveIndex: h, bMove: f, bMoveIndex: x } : { lines: o, lineCountDeleted: d, lineCountInserted: r, lineCountMoved: 0 } };
/* Usage
getDiff(oldStr, newStr)
oldStr - Old String
newStr - New String
returns diff
updateString(oldStr, diff)
oldStr - Same old String
diff - returned data from getDiff function
returns newStr
*/
function getDiff(oldStr, newStr) {
let d = patienceDiff(oldStr.split(" "), newStr.split(" "), true);
return [d.aMoveIndex, d.bMove, d.bMoveIndex]
}
function updateString(oldStr, diff) {
let str = oldStr.split(" ");
let diffObj = {};
diff[0].forEach(e => diffObj[e] = null);
diff[2].forEach((e, i) => diffObj[e] = diff[1][i]);
for (let i in diffObj)
diffObj[i] === null ? str[i] = null : str.splice(i, 0, diffObj[i])
str = str.filter(e => e != null);
return str.join(" ");
}
</script>
<script id="cc">
const cc = {
createNewArticle(title) {
const uid = floCrypto.randString(16, true)
floGlobals.appObjects.cc['defaultArticle'] = uid
floGlobals.appObjects.cc['articleList'][uid] = {
title,
timestamp: Date.now()
}
floCloudAPI.updateObjectData('cc')
.then((res) => {
console.log('created article entry', res)
})
.catch(err => console.error(err))
floGlobals.appObjects[uid] = {
public: true,
editors: [],
sections: [{
id: floCrypto.randString(16, true),
title: 'Introduction',
}, {
id: floCrypto.randString(16, true),
title: 'core',
}
]
}
floCloudAPI.resetObjectData(uid)
.then((res) => {
console.log('created article', res)
})
.catch(err => console.error(err))
}
}
getRef('article_wrapper').addEventListener('click', e => {
if (e.target.closest('.submit-entry')) {
const parent = e.target.parentNode.querySelector('.content__area')
parent.querySelectorAll('[style=""]').forEach((el) => {
el.removeAttribute('style')
})
const clean = DOMPurify.sanitize(parent.innerHTML);
floCloudAPI.sendGeneralData({
section: parent.closest('.article-section').dataset.id,
origin: parent.dataset.type === 'origin' ? floCrypto.randString(16, true) : parent.parentNode.dataset.uid,
data: parent.dataset.type === 'origin' ? clean : getDiff('', clean),
}, `${currentArticle.id}_gd`)
.then((res) => {
console.log(res)
notify('sent data', 'success')
parent.innerHTML = ''
})
}
})
getRef('article_wrapper').addEventListener("paste", e => {
const paste = (event.clipboardData || window.clipboardData).getData('text');
const selection = window.getSelection();
if (!selection.rangeCount) return false;
selection.deleteFromDocument();
selection.getRangeAt(0).insertNode(document.createTextNode(paste));
event.preventDefault();
})
getRef('article_wrapper').addEventListener("focusin", e => {
if (e.target.closest('.content__area')) {
const target = e.target.closest('.content__area')
if (target.childNodes[0] === undefined) {
target.innerHTML = `<p><br/></p>`
}
childObserver.observe(target, {
childList: true
})
}
})
getRef('article_wrapper').addEventListener("focusout", e => {
if (e.target.closest('.content__area')) {
childObserver.disconnect()
}
})
const childObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(mutation => {
if (mutation.type === 'childList' && mutation.target.childNodes[0] === undefined) {
observer.disconnect()
mutation.target.innerHTML = `<p><br/></p>`
childObserver.observe(mutation.target, {
childList: true
})
}
})
})
document.execCommand("defaultParagraphSeparator", false, "p");
function formatDoc(sCmd, sValue) {
document.execCommand(sCmd, false, sValue);
}
</script>
<script>
const CC = {
defaultArticle: '',
he78r1he74187er1h8e: 'Big Techs And Their Dominance'
}
const articles = {
he78r1he74187er1h8e: {
public: false,
editors: [],
sections: {
edg4ewr8g4e98r: {
type: 'heading',
content: 'Introduction'
},
erg981erg81eer: {
type: 'section'
},
rg8e4r98g1erg1: {
type: 'heading',
content: 'Core'
},
e9r81g8er1gerg8: {
type: 'section',
pieces: ['erg1e98r1ge9r1']
},
},
pieces: {
erg1e98r1ge9r1: {
score: 47,
content: ['ihejbn', 'ewfgjibweg']
},
rgrer1g98er1g81: {
score: 47,
paragraphs: {
gd7wed4949e8rg: ['94191981981', '89419819881', '51948191951'],
rte4h189ert1r41: ['94191981981', '89419819881', '51948191951'],
}
},
},
versionHistory: {
94191981981: {
content: `Profit margins peaked for the big techs despite the uncertainties created by the pandemic. Many companies experienced a precarious position but the tech industries shone throughout as they evolved opportunities for people. And since people were quarantined, digital platforms became a necessity for them. Hence, the digital curve accelerated with time without bouncing back, and it seems unstoppable.`,
writer: ''
},
89419819881: {
content: `FAMAG, a well-known acronym for the big techs- Facebook, Apple, Microsoft, Amazon, and Google, showed a substantial increase in its revenues and profits during the second wave. People relied more on social media platforms during the pandemic because it helped them stay in touch with each other.`,
writer: ''
},
51948191951: {
content: `Facebook, one of the many social networking sites, benefitted the most in the global turbulence. During the mid of the second Covid wave, its revenue increased by more than half, causing its profit to double. The tough times forced people to depend on online shopping, deliveries that helped giants like Amazon to perform even stronger. Its extending services, subscriptions, prime memberships, and third-party seller services have helped it to rise to new heights. Owing to the fact that people had to work from home, Microsoft Azure, a cloud computing platform, saw a surge in its services. Thus, it saw an increase in its revenues by one-fifth, which was much higher than any year.`,
writer: ''
},
98198189198: {
content: `Facebook, one of the many social networking sites, benefitted the most in the global turbulence. During the mid of the second Covid wave, its revenue increased by more than half, causing its profit to double. The tough times forced people to depend on online shopping, deliveries that helped giants like Amazon to perform even stronger. Its extending services, subscriptions, prime memberships, and third-party seller services have helped it to rise to new heights. Owing to the fact that people had to work from home, Microsoft Azure, a cloud computing platform, saw a surge in its services. Thus, it saw an increase in its revenues by one-fifth, which was much higher than any year.`,
writer: ''
}
}
}
}
let currentArticle
const render = {
heading(id) {
const { content } = articles[currentArticle].sections[id]
return createElement('h3', {
textContent: content,
className: 'heading'
})
const genData = [
{
section: 'edg4ewr8g4e98r',
origin: 'erg1e98r1ge9r1',
data: '',
score: 45,
editor: '',
timestamp: 1636485447518
},
section(id, version = 0) {
const section = document.createElement('section')
section.classList.add('section-wrapper')
const { pieces = [] } = articles[currentArticle].sections[id];
{
section: 'edg4ewr8g4e98r',
origin: 'erg1e98r1ge9r1',
data: 'u2',
score: 45,
editor: '',
timestamp: 1636485393538
},
{
section: 'edg4ewr8g4e98r',
origin: 'erg1e98r1ge9r1',
data: '',
score: 45,
editor: '',
timestamp: 1636485459328
},
{
section: 'edg4ewr8g4e98r',
origin: 'er98g4er8g1erg',
data: 'u1',
score: 45,
editor: '',
timestamp: 1636485440013
},
{
section: 'edg4ewr8g4e98r',
origin: 'erg1e98r1ge9r1',
data: '',
score: 45,
editor: '',
timestamp: 1636485477636
},
{
section: 'edg4ewr8g4e98r',
origin: 'er98g4er8g1erg',
data: '',
score: 45,
editor: '',
timestamp: 1636485484595
},
]
let currentArticle = {}
const render = {
section(sectionID, { title, uniqueEntries }) {
const section = getRef('section_template').content.cloneNode(true)
const frag = document.createDocumentFragment()
pieces.forEach(piece => {
const clone = getRef('content_card_template').content.cloneNode(true).firstElementChild;
const { paragraphs, score } = articles[currentArticle].pieces[piece]
for (const paraKey in paragraphs) {
const versions = paragraphs[paraKey]
const { content, writer } = articles[currentArticle].versionHistory[versions[version]]
const paragraph = createElement('div', {
className: 'content__area',
textContent: content,
attributes: { contentEditable: true }
})
frag.append(paragraph)
}
clone.querySelector('.paragraph-wrapper').append(frag)
clone.querySelector('.content__score').textContent = score;
section.append(clone)
})
section.children[1].dataset.id = sectionID
section.querySelector('.heading').textContent = title
currentArticle.sect
section.querySelector('.content-card-container').append(frag)
return section
},
contentCard(id, version = 0) {
const clone = getRef('content_card_template').content.cloneNode(true).firstElementChild;
const { paragraphs, score } = articles[currentArticle.id].pieces[piece]
for (const paraKey in paragraphs) {
const versions = paragraphs[paraKey]
const { content, writer } = articles[currentArticle.id].versionHistory[versions[version]]
const paragraph = createElement('div', {
className: 'content__area',
textContent: content,
attributes: { contentEditable: true }
})
frag.append(paragraph)
}
clone.querySelector('.paragraph-wrapper').append(frag)
clone.querySelector('.content__score').textContent = score;
return clone
},
article(id) {
currentArticle = id
const title = CC[id]
const { writer, sections } = articles[id]
currentArticle.id = id
parseArticleData()
const { title } = floGlobals.appObjects.cc.articleList[id]
const { writer, sections } = currentArticle
const frag = document.createDocumentFragment()
let paraGroup
let isMultiParagraphed = false
for (const nodeKey in sections) {
const { type } = sections[nodeKey]
switch (type) {
case 'heading':
frag.append(render.heading(nodeKey))
break;
case 'section':
frag.append(render.section(nodeKey))
break;
}
for (const sectionID in sections) {
frag.append(render.section(sectionID, sections[sectionID]))
}
getRef('current_article_name').textContent = title
getRef('article_wrapper').innerHTML = ''
getRef('article_wrapper').append(frag)
}
}
render.article('he78r1he74187er1h8e')
// getRef('article_wrapper').addEventListener('paste', e => {
// let paste = (e.clipboardData || window.clipboardData).getData('text');
// document.execCommand('insertText', false, paste)
// e.preventDefault()
// })
function parseArticleData() {
const { sections, editors, public } = floGlobals.appObjects[currentArticle.id]
const generalData = floGlobals.generalData[`${currentArticle.id}_gd|${floGlobals.adminID}|${floGlobals.application}`]
currentArticle['sections'] = {}
sections.forEach(({ id, title }) => {
currentArticle['sections'][id] = {
title,
uniqueEntries: []
}
})
currentArticle['uniqueEntries'] = {}
for (const key in generalData) {
const { section, data, origin } = generalData[key].message
if (!currentArticle.uniqueEntries.hasOwnProperty(origin)) {
currentArticle.uniqueEntries[origin] = {
iterations: []
}
}
currentArticle.sections[section].uniqueEntries.push(origin)
currentArticle.uniqueEntries[origin]['iterations'].push({
timestamp: generalData[key].time,
data
})
}
for (const entry in currentArticle.uniqueEntries) {
currentArticle.uniqueEntries[entry]['iterations'].sort((a, b) => a.timestamp - b.timestamp)
}
}
const splitAt = (string, index) => [string.slice(0, index), string.slice(index)]
getRef('article_wrapper').addEventListener('keydown', e => {
if (e.key === 'Enter' && e.target.closest('.content__area')) {
e.preventDefault()
const selection = window.getSelection()
const startIndex = selection.anchorOffset
const newParas = splitAt(e.target.innerText, startIndex)
if (newParas.length === 1) {
if (newParas[0].trim() !== '') {
e.target.textContent = e.target.innerText.trim()
const paragraph = createElement('div', {
className: 'content__area',
textContent: '',
attributes: { contentEditable: true }
})
e.target.after(paragraph)
e.target.nextElementSibling.focus()
}
} else {
newParas.forEach((para, index) => {
if (index === 0) {
e.target.textContent = para
} else {
const paragraph = createElement('div', {
className: 'content__area',
textContent: para.trim(),
attributes: { contentEditable: true }
})
e.target.after(paragraph)
e.target.nextElementSibling.focus()
}
})
}
}
})
getRef('article_wrapper').addEventListener('keyup', e => {
if ((e.key === 'Backspace' || e.key === 'Delete') && e.target.closest('.content__area')) {
if (e.target.innerText === '' && e.target.parentNode.children.length > 1) {
if (e.target.previousElementSibling) {
e.target.previousElementSibling.focus()
const range = document.createRange();
range.selectNodeContents(e.target.previousElementSibling);
range.collapse(false);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
const target = e.target
target.animate([
{
opacity: 1
},
{
opacity: 0
},
], {
duration: 100,
fill: 'forwards'
})
.onfinish = () => {
const height = target.getBoundingClientRect().height
const siblings = Array.from(target.parentNode.children)
const nextSiblings = siblings.slice(siblings.indexOf(target) + 1)
nextSiblings.forEach(elem => {
elem.animate([
{
transform: 'translateY(0)'
},
{
transform: `translateY(-${height}px)`
},
], {
duration: 100,
fill: 'forwards'
})
.onfinish = (e) => {
e.target.cancel()
}
})
setTimeout(() => {
target.remove()
}, 100);
}
}
}
})
getRef('article_wrapper').addEventListener('focusout', e => {
if (e.target.closest('.content__area')) {
normalizeText(e.target.closest('.content__area'))
@ -826,9 +917,15 @@
const detectFormatting = debounce(() => {
const selection = window.getSelection();
if (selection.isCollapsed) {
getRef('text_toolbar').classList.add('hide-completely')
document.querySelectorAll('.formatting-button').forEach(elem => elem.classList.remove('active'))
} else {
const { isBold, isItalic, isUnderlined } = getSelectionFormatting()
getRef('text_toolbar').classList.remove('hide-completely')
const pos = selection.getRangeAt(0).getBoundingClientRect()
getRef('text_toolbar').style.transform = `translate(${pos.left}px, calc(${pos.bottom + window.pageYOffset}px + 0.3rem))`
const isBold = document.queryCommandState('bold')
const isItalic = document.queryCommandState('italic')
const isUnderlined = document.queryCommandState('underline')
if (isBold) {
getRef(`strong_button`).classList.add('active')
} else {
@ -846,7 +943,7 @@
}
}
},
300)
200)
document.addEventListener('selectionchange', detectFormatting)
@ -854,10 +951,10 @@
`${origin.substring(0, startIndex)}${insertion}${origin.substring(endIndex)}`;
const make = {
bold() {
replaceSelectedText(createElement('strong'))
document.execCommand('bold')
},
italic() {
replaceSelectedText(createElement('em'))
document.execCommand('italic')
},
underlined() {
replaceSelectedText(createElement('u'))
@ -895,12 +992,6 @@
selection.anchorNode.parentNode.closest('.content__area')?.focus()
}
}
document.addEventListener('keydown', e => {
if (e.ctrlKey && e.key === 'b') {
make.bold()
e.preventDefault()
}
})
function getSelectionFormatting() {
const sel = window.getSelection();
@ -960,12 +1051,6 @@
return !array2.some(elem => !array1.includes(elem))
}
const html = `Profit margins peaked for the big techs despite the uncertainties created by the pandemic. Many companies experienced a precarious position but the tech industries shone throughout as they evolved opportunities for people. And since people were quarantined, digital platforms became a necessity for them. Hence, the digital curve accelerated with time without bouncing back, and it seems unstoppable.
Profit margins peaked for the big techs despite the uncertainties created by the pandemic. Many companies experienced a precarious position but the tech industries shone throughout as they evolved opportunities for people. And since people were quarantined, digital platforms became a necessity for them. Hence, the digital curve accelerated with time without bouncing back, and it seems unstoppable.
<figure><blockquote cite="https://www.huxley.net/bnw/four.html"><p>Words can be like X-rays, if you use them properly—theyll go through anything. You read and youre pierced.</p></blockquote> <figcaption>—Aldous Huxley, <cite>Brave New World</cite></figcaption> </figure>
Profit margins peaked for the big techs despite the uncertainties created by the pandemic. Many companies experienced a precarious position but the tech industries shone throughout as they evolved opportunities for people. And since people were quarantined, digital platforms became a necessity for them. Hence, the digital curve accelerated with time without bouncing back, and it seems unstoppable.`
console.log(html.split('\n'))
function insertNode(node) {
const selection = window.getSelection();
if (selection.rangeCount) {
@ -976,27 +1061,13 @@
function createQuote() {
const quote = getRef('quote_template').content.cloneNode(true)
quote.querySelector('.quote').textContent = '"Words can be like X-rays, if you use them properly—theyll go through anything. You read and youre pierced."'
quote.querySelector('.by').textContent = 'Aldous Huxley'
quote.querySelector('.by').textContent = `- Author`
quote.querySelector('.citation').textContent = 'Brave New World'
return quote
}
function insertBlockquote() {
insertNode(createQuote())
}
function createNewArticle() {
const uid = floCrypto.randString(16, true)
floGlobals.appObjects['articlesIndex'][uid] = {
title: 'test article 1',
timestamp: Date.now()
}
console.log(floGlobals.appObjects.articlesIndex)
floCloudAPI.updateObjectData('articlesIndex')
.then((res) => {
console.log('created article', res)
})
.catch(err => console.error(err))
}
</script>
<script id="onLoadStartUp">
function onLoadStartUp() {
@ -1008,11 +1079,17 @@
floDapps.launchStartUp().then(async result => {
await Promise.all([
floCloudAPI.requestObjectData('cc'),
floCloudAPI.requestObjectData('defaultArticle'),
floCloudAPI.requestObjectData('articlesIndex'),
])
Promise.all([
floCloudAPI.requestObjectData(floGlobals.appObjects.cc.defaultArticle),
floCloudAPI.requestGeneralData(`${floGlobals.appObjects.cc.defaultArticle}_gd`)
])
.then(() => {
render.article(floGlobals.appObjects.cc.defaultArticle)
})
console.log(result)
// alert(`Welcome FLO_ID: ${myFloID}`)
// alert(`Welcome FLO_ID: ${ myFloID }`)
//App functions....
}).catch(error => console.error(error))
}

13
package-lock.json generated Normal file
View File

@ -0,0 +1,13 @@
{
"name": "p2p-content-collaboration",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"tiny-editor": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/tiny-editor/-/tiny-editor-0.2.5.tgz",
"integrity": "sha512-K4luWQbam/TrgjLst+Ztb7uPltXHwtFm+Oi1hs3xM6biI7N1X8HfyDtmROOEbyJM8+mMP5/FjsMnc96zJZoZCg=="
}
}
}

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "p2p-content-collaboration",
"version": "1.0.0",
"description": "",
"main": "components.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ranchimall/p2p-content-collaboration.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/ranchimall/p2p-content-collaboration/issues"
},
"homepage": "https://github.com/ranchimall/p2p-content-collaboration#readme",
"dependencies": {
"tiny-editor": "^0.2.5"
}
}