Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2689966
D25577.1734742326.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
53 KB
Referenced Files
None
Subscribers
None
D25577.1734742326.diff
View Options
diff --git a/.arcconfig b/.arcconfig
new file mode 100644
--- /dev/null
+++ b/.arcconfig
@@ -0,0 +1,6 @@
+{
+ "phabricator.uri": "https://we.phorge.it/",
+ "load": [
+ "src/"
+ ]
+}
diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,2 @@
-/data/drawio
-
-/src/__phutil_library_init__.php
-/src/__phutil_library_map__.php
+/drawio
/src/.phutil_module_cache
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -3,12 +3,19 @@
Installation
============
-1) Extract the content of this repository into <phorge>/src/extensions
-2) <arcanist>/bin/arc liberate
-3) <phorge>/bin/storage upgrade
-4) CD to <phorge>/src/extensions/drawio/data
-5) git clone https://github.com/jgraph/drawio.git
-6) Diagrams application is available under "More Applications" in Phorge.
+1) `git clone` this repository somewhere safe: `</somewhere/safe/diagrams>`
+2) CD to `</somewhere/safe/diagrams>` and `git clone https://github.com/jgraph/drawio.git && cd drawio && git switch v24.2.5`
+3) `<phorge>/bin/storage upgrade`
+4) In Phorge's `conf/local/local.json` add the path to the `src/` dir to the entry `load-libraries`,
+ something like this:
+```
+ ...
+ "load-libraries": [
+ "/somewhere/safe/diagrams/src/"
+ ],
+ ...
+```
+5) Diagrams application is available under "More Applications" in Phorge.
You may add it to your navigator menu via "Edit Menu"
Usage
diff --git a/data/iframe.css b/data/iframe.css
deleted file mode 100644
--- a/data/iframe.css
+++ /dev/null
@@ -1,97 +0,0 @@
-body { overflow:hidden; }
-div.picker { z-index: 10007; }
-.geSidebarContainer .geTitle input {
- font-size:8pt;
- color:#606060;
-}
-.geBlock {
- z-index:-3;
- margin:100px;
- margin-top:40px;
- margin-bottom:30px;
- padding:20px;
- text-align:center;
- min-width:50%;
-}
-.geBlock h1, .geBlock h2 {
- margin-top:0px;
- padding-top:0px;
-}
-.geEditor *:not(.geScrollable)::-webkit-scrollbar {
- width:10px;
- height:10px;
-}
-.geEditor ::-webkit-scrollbar-track {
- background-clip:padding-box;
- border:solid transparent;
- border-width:1px;
-}
-.geEditor ::-webkit-scrollbar-corner {
- background-color:transparent;
-}
-.geEditor ::-webkit-scrollbar-thumb {
- background-color:rgba(0,0,0,.1);
- background-clip:padding-box;
- border:solid transparent;
- border-radius:10px;
-}
-.geEditor ::-webkit-scrollbar-thumb:hover {
- background-color:rgba(0,0,0,.4);
-}
-.geTemplate {
- border:1px solid transparent;
- display:inline-block;
- _display:inline;
- vertical-align:top;
- border-radius:3px;
- overflow:hidden;
- font-size:14pt;
- cursor:pointer;
- margin:5px;
-}
-
-@keyframes init-spinner-animation {
- 0%, 39%, 100% {
- opacity: 0.33;
- }
- 40% {
- opacity: .83;
- }
-}
-.init-spinner {
- position: relative;
- height: 80px;
- width: 80px;
- left: calc(50% - 50px);
-}
-.init-spinner .content {
- position: absolute;
- width: 0px;
- left: 50%;
- top: 50%;
-}
-
-.init-spinner .content .spike {
- position: absolute;
- top: -2.5px;
- width: 15px;
- height: 5px;
- background: #000c;
- border-radius: 2.5px;
- transform-origin: left center 0px;
-}
-
-.init-spinner .content .spike .animator {
- width: 100%;
- height: 100%;
- background: rgb(255, 255, 255);
- border-radius: 2.5px;
- box-shadow: transparent 0px 0px 1px;
- animation-name: init-spinner-animation;
- animation-duration: 1s;
- animation-timing-function: linear;
- animation-iteration-count: infinite;
- animation-direction: normal;
- animation-fill-mode: none;
- animation-play-state: running;
-}
\ No newline at end of file
diff --git a/data/iframe1.js b/data/iframe1.js
deleted file mode 100644
--- a/data/iframe1.js
+++ /dev/null
@@ -1,144 +0,0 @@
-var urlParams = (function() {
- var result = {};
- var params = window.location.search.slice(1).split('&');
-
- for (var i = 0; i < params.length; i++) {
- var idx = params[i].indexOf('=');
-
- if (idx > 0) {
- result[params[i].substring(0, idx)] = params[i].substring(idx + 1);
- }
- }
-
- return result;
-})();
-
-if (window.location.hash != null && window.location.hash.substring(0, 2) == '#P') {
- try {
- urlParams = JSON.parse(decodeURIComponent(window.location.hash.substring(2)));
-
- if (urlParams.hash != null) {
- window.location.hash = urlParams.hash;
- }
- }
- catch (e) {
- }
-}
-
-(function() {
- var proto = window.location.protocol;
- var host = window.location.host;
- var href = proto + '//' + host + window.location.href.substring(
- window.location.protocol.length +
- window.location.host.length + 2);
-
- if (href != window.location.href) {
- window.location.href = href;
- }
-})();
-
-function mxmeta(name, content, httpEquiv) {
- try {
- var s = document.createElement('meta');
-
- if (name != null) {
- s.setAttribute('name', name);
- }
-
- s.setAttribute('content', content);
-
- if (httpEquiv != null) {
- s.setAttribute('http-equiv', httpEquiv);
- }
-
- var t = document.getElementsByTagName('meta')[0];
- t.parentNode.insertBefore(s, t);
- }
- catch (e)
- {
- }
-}
-
-function mxscript(src, onLoad, id, dataAppKey, noWrite, onError) {
- var s = document.createElement('script');
- s.setAttribute('type', 'text/javascript');
- s.setAttribute('defer', 'true');
- s.setAttribute('src', src);
-
- if (id != null) {
- s.setAttribute('id', id);
- }
-
- if (dataAppKey != null) {
- s.setAttribute('data-app-key', dataAppKey);
- }
-
- if (onLoad != null) {
- var r = false;
-
- s.onload = s.onreadystatechange = function() {
- if (!r && (!this.readyState || this.readyState == 'complete')) {
- r = true;
- onLoad();
- }
- };
- }
-
- if (onError != null) {
- s.onerror = function(e) {
- onError('Failed to load ' + src, e);
- };
- }
-
- var t = document.getElementsByTagName('script')[0];
-
- if (t != null) {
- t.parentNode.insertBefore(s, t);
- }
-}
-
-function mxinclude(src) {
- var g = document.createElement('script');
- g.type = 'text/javascript';
- g.async = true;
- g.src = src;
-
- var s = document.getElementsByTagName('script')[0];
- s.parentNode.insertBefore(g, s);
-}
-
-(function() {
- var name = 'diagrams.net';
- mxmeta('apple-mobile-web-app-title', name);
- mxmeta('application-name', name);
-})();
-
-var isLocalStorage = true;
-var mxScriptsLoaded = false, mxWinLoaded = false;
-
-function checkAllLoaded() {
- if (mxScriptsLoaded && mxWinLoaded) {
- App.main();
- }
-}
-
-var t0 = new Date();
- (function() {
- function loadAppJS() {
- mxscript('js/app.min.js', function() {
- mxScriptsLoaded = true;
- checkAllLoaded();
- mxscript('js/PostConfig.js');
- });
- }
-
- mxscript('js/PreConfig.js', loadAppJS);
-})();
-
-window.onerror = function() {
- var status = document.getElementById('geStatus');
-
- if (status != null) {
- status.innerHTML = 'Page could not be loaded. Please try refreshing.';
- }
-};
diff --git a/data/iframe2.js b/data/iframe2.js
deleted file mode 100644
--- a/data/iframe2.js
+++ /dev/null
@@ -1,7 +0,0 @@
-window.addEventListener('load', function()
-{
- mxWinLoaded = true;
- checkAllLoaded();
-});
-
-phorge_extension = parent.phorge_extension;
\ No newline at end of file
diff --git a/data/phorge_extension.js b/data/phorge_extension.js
deleted file mode 100644
--- a/data/phorge_extension.js
+++ /dev/null
@@ -1,246 +0,0 @@
-var phorge_extension = {};
-
-function edit(image) {
- var iframe = document.createElement('iframe');
- var iframeInitializedEvent = new CustomEvent('initReceived');
- iframe.setAttribute('title', 'diagrams.net editor');
- iframe.setAttribute('frameborder', '0');
- iframe.style.width = '100%';
- iframe.style.height = 'calc(100vh - 91px)';
- iframe.style.marginTop = '30px';
- iframe.style.marginBottom = '-16px';
- image.style.display = 'none';
-
- var receive = function (evt) {
- if (evt.data.length > 0) {
- var msg = JSON.parse(evt.data);
- if (msg.event == 'init') {
- if (phorge_extension.diagramBase64) {
- image.src = 'data:image/png;base64,' + phorge_extension.diagramBase64;
-
- var diagramName = document.querySelector('.diagramName');
- diagramName.style.display = 'inline-block';
- diagramName.querySelector('a').innerText = phorge_extension.diagramName;
- if (phorge_extension.diagramVersion != '') {
- diagramName.querySelector('.version').innerText = '(#'
- + phorge_extension.diagramVersion
- + ')';
- }
- }
-
- iframe.contentWindow.postMessage(JSON.stringify({
- action: 'load',
- autosave: 1,
- xmlpng: image.getAttribute('src')
- }), '*');
- }
- else if (msg.event == 'load') {
- // enable Mathematical Typesettings by default
- iframe.contentWindow.sb.editorUi.setMathEnabled(true);
-
- setupButtonsInMenuToolbar();
- }
- else if (msg.event == 'export') {
- saveFlowchart(name, msg.data, iframe);
- }
- else if (msg.event == 'save') {
- iframe.contentWindow.postMessage(JSON.stringify({
- action: 'export',
- format: 'xmlpng',
- xml: msg.xml,
- spin: 'Updating page'
- }), '*');
- }
- }
- };
-
- window.addEventListener('message', receive);
- iframe.setAttribute('src', phorge_extension.editor);
- document.querySelector('#mainScreen').appendChild(iframe);
- iframe.dispatchEvent(iframeInitializedEvent);
-
- iframe.contentWindow.RESOURCES_PATH = document.baseURI + 'iframe/resources';
- iframe.contentWindow.STENCIL_PATH = document.baseURI + 'iframe/stencils';
- iframe.contentWindow.IMAGE_PATH = document.baseURI + 'iframe/images';
- iframe.contentWindow.STYLE_PATH = document.baseURI + 'iframe/styles';
- iframe.contentWindow.CSS_PATH = document.baseURI + 'iframe/styles';
-}
-
-function loadJsExtension(diagramName, diagramPHID, diagramVersion, diagramBase64) {
- var baseURI = document.baseURI;
- if (diagramName != '' && diagramBase64 != '') {
- baseURI = baseURI.substr(0, baseURI.length - diagramName.length - 1);
-
- if (diagramVersion != '') {
- baseURI = baseURI.substr(0, baseURI.length - diagramVersion.length - 1);
- }
- }
- phorge_extension.baseURI = baseURI;
- phorge_extension.csrf = document.querySelector('input[name="__csrf__"]')?.value;
- phorge_extension.editor = baseURI + '/iframe/?embed=1&spin=0&proto=json&noExitBtn=1';
- phorge_extension.name = null;
- phorge_extension.editor += '&lang=en';
- phorge_extension.editor += '&ui=min';
- phorge_extension.diagramPHID = diagramPHID;
- phorge_extension.diagramName = diagramName;
- phorge_extension.diagramVersion = diagramVersion;
- phorge_extension.diagramBase64 = diagramBase64;
-
- document.addEventListener('DOMContentLoaded', function () {
- edit(document.querySelector('img.drawio'));
- }, false);
-}
-
-function saveFlowchart(name, flowchartData, iframe) {
- var diagramID = document.querySelector('.diagramName a').innerText.replace(/^DIAG/, '');
- var csrf = document.querySelector('input[name="__csrf__"]')?.value;
- var data = new URLSearchParams();
- data.append('data', flowchartData);
- data.append('diagramID', diagramID);
- data.append('__csrf__', csrf);
- data.append('__form__', '1');
- data.append('__ajax__', 'true');
-
- var xmlhttp = new XMLHttpRequest();
- xmlhttp.overrideMimeType('application/json');
- xmlhttp.open('POST', 'save/', true);
- xmlhttp.onload = function () {
- if (xmlhttp.readyState == 4) {
- var errorMessage = null;
- try {
- var result = JSON.parse(xmlhttp.responseText);
- if (result.Status != 'OK') {
- errorMessage = result.Error;
- } else {
- // make sure we don't show messagebox about redirection in browser
- iframe.parentNode.removeChild(iframe);
-
- if (!phorge_extension.diagramVersion || !phorge_extension.diagramVersion.trim()) {
- if (!phorge_extension.diagramName || !phorge_extension.diagramName.trim()) {
- // load new diagram
- window.location = window.location + '/DIAG' + result.DiagramID;
- } else {
- // reload actual page (so versioned diagrams info is also updated)
- window.location.reload();
- }
- } else {
- // cut off version id from url
- var url = document.baseURI
- .substring(0,
- document.baseURI
- .length
- - phorge_extension.diagramVersion
- .length
- - 1
- );
- // redirect to latest version of diagram
- window.location = url;
- }
- }
- } catch (exc) {
- errorMessage = exc.message;
- }
- }
- };
-
- xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;');
- xmlhttp.send(data);
-}
-
-function setupButtonsInMenuToolbar() {
- var iframe = document.querySelector('iframe');
- var btnSave = Array.prototype.slice.call(
- iframe.contentDocument
- .querySelector('.geMenubarContainer')
- .querySelectorAll('button'), 0
- ).reverse()[0];
-
- // identify Exit Button
- btnSave.classList.add('btnSave');
-
- // change layout settings of area where btnSave belongs to so
- // that the dropdown menu is not hidden under the drawing area
- btnSave.parentNode.style.position = 'fixed';
-
- // create extra controls
- var subscribeUnsubscribe = setupSubscriptionButtonInMenuToolbar(iframe, btnSave);
- var dropdown = setupVersionDropDownInMenuToolbar(iframe, subscribeUnsubscribe);
-
- // initialize toolbuttons
- for (var tb in phorge_extension.toolbtn) {
- if (phorge_extension.toolbtn.hasOwnProperty(tb)) {
- phorge_extension.toolbtn[tb].instance.initialize();
- }
- }
-}
-
-function setupVersionDropDownInMenuToolbar(iframe, btnLeft) {
- // generate 'Select Version' button
- // create the dropdown element
- const dropdown = document.createElement('div');
- dropdown.classList.add('toolbtn-version-dropdown');
-
- // create the dropdown toggle button
- const toggle = document.createElement('button');
- toggle.classList.add('toolbtn-version-dropdown-toggle');
- toggle.textContent = 'Select Version';
- toggle.title = 'View previous versions';
- dropdown.appendChild(toggle);
-
- const iframeContent = document.querySelector('iframe').contentDocument;
- const menubarContainer = iframeContent.querySelector('.geMenubarContainer');
-
- // create the dropdown menu
- const menu = document.createElement('div');
- menu.classList.add('toolbtn-version-dropdown-menu');
- menubarContainer.parentNode.insertBefore(menu, menubarContainer.nextSibling);
-
- // create the version list
- const versionList = document.createElement('ul');
- versionList.classList.add('toolbtn-version-list');
-
- menu.appendChild(versionList);
-
- // create the prev and next buttons
- const prevBtn = document.createElement('button');
- prevBtn.classList.add('toolbtn-version-prev-btn');
- prevBtn.textContent = '<';
-
- const nextBtn = document.createElement('button');
- nextBtn.classList.add('toolbtn-version-next-btn');
- nextBtn.textContent = '>';
-
- menu.appendChild(prevBtn);
- menu.appendChild(nextBtn);
-
- // place dropdown next to button on the left
- btnLeft.parentNode.insertBefore(dropdown, btnLeft.nextSibling);
-
- return dropdown;
-}
-
-function setupSubscriptionButtonInMenuToolbar(iframe, btnLeft) {
- // generate 'Subscribe/Unsubscribe' button
- // create grouping div element
- const div = document.createElement('div');
- div.classList.add('toolbtn-diagram-subscription');
-
- // create subscribe button
- const btnSubscribe = document.createElement('button');
- btnSubscribe.classList.add('subscribe');
- btnSubscribe.classList.add('eye');
- btnSubscribe.title = 'Subscribe';
- div.appendChild(btnSubscribe);
-
- // create unsubscribe button
- const btnUnsubscribe = document.createElement('button');
- btnUnsubscribe.classList.add('unsubscribe');
- btnUnsubscribe.classList.add('eye');
- btnUnsubscribe.title = 'Unsubscribe';
- div.appendChild(btnUnsubscribe);
-
- // place grouping next to button on the left
- btnLeft.parentNode.insertBefore(div, btnLeft.nextSibling);
-
- return div;
-}
\ No newline at end of file
diff --git a/data/iframe-toolbtn.css b/rsrc/iframe-toolbtn.css
rename from data/iframe-toolbtn.css
rename to rsrc/iframe-toolbtn.css
--- a/data/iframe-toolbtn.css
+++ b/rsrc/iframe-toolbtn.css
@@ -1,4 +1,8 @@
-/* begin stylesheets for version toolbutton */
+/**
+ * @provides diagram-css-iframe-toolbtn
+ */
+
+/* begin stylesheets for version toolbutton */
.toolbtn-version-dropdown {
right: 0;
white-space: nowrap;
diff --git a/data/iframe-toolbtn.js b/rsrc/iframe-toolbtn.js
rename from data/iframe-toolbtn.js
rename to rsrc/iframe-toolbtn.js
--- a/data/iframe-toolbtn.js
+++ b/rsrc/iframe-toolbtn.js
@@ -1,8 +1,14 @@
+/**
+ * @provides diagram-js-iframe-toolbtn
+ */
+
/**
* global dicttionary containing all configuration of all installed toolbuttons
*/
const toolbtn = {};
+const phorge_extension = parent.__diagram;
+
/**
* Abstract parent class for creating ToolButtons
*/
@@ -15,7 +21,8 @@
toolbtn[name] = {};
toolbtn[name].instance = this;
- parent.phorge_extension.toolbtn = toolbtn;
+ phorge_extension.toolbtn = toolbtn;
+ console.log(phorge_extension);
}
initialize() {
@@ -248,4 +255,4 @@
* Initialization of all ToolButton classes
*/
new ToolButtonVersion('version');
-new ToolButtonSubscription('subscription');
\ No newline at end of file
+new ToolButtonSubscription('subscription');
diff --git a/rsrc/phorge_extension.js b/rsrc/phorge_extension.js
new file mode 100644
--- /dev/null
+++ b/rsrc/phorge_extension.js
@@ -0,0 +1,276 @@
+/**
+ * @provides javelin-behavior-diagram-extension
+ */
+
+JX.behavior('diagram-extension', function(config) {
+
+ var phorge_extension = {};
+
+ function edit(image) {
+ var iframe = document.createElement('iframe');
+ var iframeInitializedEvent = new CustomEvent('initReceived');
+ iframe.setAttribute('title', 'diagrams.net editor');
+ iframe.setAttribute('frameborder', '0');
+ iframe.style.width = '100%';
+ iframe.style.height = 'calc(100vh - 91px)';
+ iframe.style.marginTop = '30px';
+ iframe.style.marginBottom = '-16px';
+ image.style.display = 'none';
+
+ var receive = function (evt) {
+ if (evt.data.length > 0) {
+ var msg = JSON.parse(evt.data);
+ if (msg.event == 'init') {
+ if (phorge_extension.diagramBase64) {
+ image.src = 'data:image/png;base64,' + phorge_extension.diagramBase64;
+
+ var diagramName = document.querySelector('.diagramName');
+ diagramName.style.display = 'inline-block';
+ diagramName.querySelector('a').innerText = phorge_extension.diagramName;
+ if (phorge_extension.diagramVersion != '') {
+ diagramName.querySelector('.version').innerText = '(#'
+ + phorge_extension.diagramVersion
+ + ')';
+ }
+ }
+
+ iframe.contentWindow.postMessage(JSON.stringify({
+ action: 'load',
+ autosave: 1,
+ xmlpng: image.getAttribute('src')
+ }), '*');
+ }
+ else if (msg.event == 'load') {
+ // enable Mathematical Typesettings by default
+ iframe.contentWindow.sb.editorUi.setMathEnabled(true);
+
+ setupButtonsInMenuToolbar();
+ }
+ else if (msg.event == 'export') {
+ saveFlowchart(name, msg.data, iframe);
+ }
+ else if (msg.event == 'save') {
+ iframe.contentWindow.postMessage(JSON.stringify({
+ action: 'export',
+ format: 'xmlpng',
+ xml: msg.xml,
+ spin: 'Updating page'
+ }), '*');
+ }
+ }
+ };
+
+ window.addEventListener('message', receive);
+ iframe.setAttribute('src', phorge_extension.editor);
+ document.querySelector('#mainScreen').appendChild(iframe);
+ iframe.contentWindow.addEventListener('DOMContentLoaded', addToolbarResourcesToIframe);
+ iframe.dispatchEvent(iframeInitializedEvent);
+
+ iframe.contentWindow.RESOURCES_PATH = document.baseURI + 'iframe/resources';
+ iframe.contentWindow.STENCIL_PATH = document.baseURI + 'iframe/stencils';
+ iframe.contentWindow.IMAGE_PATH = document.baseURI + 'iframe/images';
+ iframe.contentWindow.STYLE_PATH = document.baseURI + 'iframe/styles';
+ iframe.contentWindow.CSS_PATH = document.baseURI + 'iframe/styles';
+ }
+
+ function addToolbarResourcesToIframe() {
+ var head = this.document.getElementsByTagName('head')[0];
+
+ var script = this.document.createElement('script');
+ script.src = config.toolbarJs;
+ head.appendChild(script);
+
+ var styles = this.document.createElement('link');
+ styles.setAttribute('type', 'text/css');
+ styles.setAttribute('rel', 'stylesheet');
+ styles.setAttribute('href', config.toolbarCss);
+ head.appendChild(styles);
+ }
+
+ function loadJsExtension(diagramName, diagramPHID, diagramVersion, diagramBase64) {
+ var baseURI = document.baseURI;
+ if (diagramName != '' && diagramBase64 != '') {
+ baseURI = baseURI.substr(0, baseURI.length - diagramName.length - 1);
+
+ if (diagramVersion != '') {
+ baseURI = baseURI.substr(0, baseURI.length - diagramVersion.length - 1);
+ }
+ }
+ phorge_extension.baseURI = baseURI;
+ phorge_extension.csrf = document.querySelector('input[name="__csrf__"]')?.value;
+ phorge_extension.editor = baseURI + '/iframe/?embed=1&spin=0&proto=json&noExitBtn=1';
+ phorge_extension.name = null;
+ phorge_extension.editor += '&lang=en';
+ phorge_extension.editor += '&ui=min';
+ phorge_extension.diagramPHID = diagramPHID;
+ phorge_extension.diagramName = diagramName;
+ phorge_extension.diagramVersion = diagramVersion;
+ phorge_extension.diagramBase64 = diagramBase64;
+
+ document.addEventListener('DOMContentLoaded', function () {
+ edit(document.querySelector('img.drawio'));
+ }, false);
+ }
+
+ function saveFlowchart(name, flowchartData, iframe) {
+ var diagramID = document.querySelector('.diagramName a').innerText.replace(/^DIAG/, '');
+ var csrf = document.querySelector('input[name="__csrf__"]')?.value;
+ var data = new URLSearchParams();
+ data.append('data', flowchartData);
+ data.append('diagramID', diagramID);
+ data.append('__csrf__', csrf);
+ data.append('__form__', '1');
+ data.append('__ajax__', 'true');
+
+ var xmlhttp = new XMLHttpRequest();
+ xmlhttp.overrideMimeType('application/json');
+ xmlhttp.open('POST', 'save/', true);
+ xmlhttp.onload = function () {
+ if (xmlhttp.readyState == 4) {
+ var errorMessage = null;
+ try {
+ var result = JSON.parse(xmlhttp.responseText);
+ if (result.Status != 'OK') {
+ errorMessage = result.Error;
+ } else {
+ // make sure we don't show messagebox about redirection in browser
+ iframe.parentNode.removeChild(iframe);
+
+ if (!phorge_extension.diagramVersion || !phorge_extension.diagramVersion.trim()) {
+ if (!phorge_extension.diagramName || !phorge_extension.diagramName.trim()) {
+ // load new diagram
+ window.location = window.location + '/DIAG' + result.DiagramID;
+ } else {
+ // reload actual page (so versioned diagrams info is also updated)
+ window.location.reload();
+ }
+ } else {
+ // cut off version id from url
+ var url = document.baseURI
+ .substring(0,
+ document.baseURI
+ .length
+ - phorge_extension.diagramVersion
+ .length
+ - 1
+ );
+ // redirect to latest version of diagram
+ window.location = url;
+ }
+ }
+ } catch (exc) {
+ errorMessage = exc.message;
+ }
+ }
+ };
+
+ xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;');
+ xmlhttp.send(data);
+ }
+
+ function setupButtonsInMenuToolbar() {
+ console.log('setupButtonsInMenuToolbar');
+ var iframe = document.querySelector('iframe');
+ var btnSave = Array.prototype.slice.call(
+ iframe.contentDocument
+ .querySelector('.geMenubarContainer')
+ .querySelectorAll('button'), 0
+ ).reverse()[0];
+
+ // identify Exit Button
+ btnSave.classList.add('btnSave');
+
+ // change layout settings of area where btnSave belongs to so
+ // that the dropdown menu is not hidden under the drawing area
+ btnSave.parentNode.style.position = 'fixed';
+
+ // create extra controls
+ var subscribeUnsubscribe = setupSubscriptionButtonInMenuToolbar(iframe, btnSave);
+ var dropdown = setupVersionDropDownInMenuToolbar(iframe, subscribeUnsubscribe);
+
+ // initialize toolbuttons
+ for (var tb in phorge_extension.toolbtn) {
+ if (phorge_extension.toolbtn.hasOwnProperty(tb)) {
+ phorge_extension.toolbtn[tb].instance.initialize();
+ }
+ }
+ }
+
+ function setupVersionDropDownInMenuToolbar(iframe, btnLeft) {
+ // generate 'Select Version' button
+ // create the dropdown element
+ const dropdown = document.createElement('div');
+ dropdown.classList.add('toolbtn-version-dropdown');
+
+ // create the dropdown toggle button
+ const toggle = document.createElement('button');
+ toggle.classList.add('toolbtn-version-dropdown-toggle');
+ toggle.textContent = 'Select Version';
+ toggle.title = 'View previous versions';
+ dropdown.appendChild(toggle);
+
+ const iframeContent = document.querySelector('iframe').contentDocument;
+ const menubarContainer = iframeContent.querySelector('.geMenubarContainer');
+
+ // create the dropdown menu
+ const menu = document.createElement('div');
+ menu.classList.add('toolbtn-version-dropdown-menu');
+ menubarContainer.parentNode.insertBefore(menu, menubarContainer.nextSibling);
+
+ // create the version list
+ const versionList = document.createElement('ul');
+ versionList.classList.add('toolbtn-version-list');
+
+ menu.appendChild(versionList);
+
+ // create the prev and next buttons
+ const prevBtn = document.createElement('button');
+ prevBtn.classList.add('toolbtn-version-prev-btn');
+ prevBtn.textContent = '<';
+
+ const nextBtn = document.createElement('button');
+ nextBtn.classList.add('toolbtn-version-next-btn');
+ nextBtn.textContent = '>';
+
+ menu.appendChild(prevBtn);
+ menu.appendChild(nextBtn);
+
+ // place dropdown next to button on the left
+ btnLeft.parentNode.insertBefore(dropdown, btnLeft.nextSibling);
+
+ return dropdown;
+ }
+
+ function setupSubscriptionButtonInMenuToolbar(iframe, btnLeft) {
+ // generate 'Subscribe/Unsubscribe' button
+ // create grouping div element
+ const div = document.createElement('div');
+ div.classList.add('toolbtn-diagram-subscription');
+
+ // create subscribe button
+ const btnSubscribe = document.createElement('button');
+ btnSubscribe.classList.add('subscribe');
+ btnSubscribe.classList.add('eye');
+ btnSubscribe.title = 'Subscribe';
+ div.appendChild(btnSubscribe);
+
+ // create unsubscribe button
+ const btnUnsubscribe = document.createElement('button');
+ btnUnsubscribe.classList.add('unsubscribe');
+ btnUnsubscribe.classList.add('eye');
+ btnUnsubscribe.title = 'Unsubscribe';
+ div.appendChild(btnUnsubscribe);
+
+ // place grouping next to button on the left
+ btnLeft.parentNode.insertBefore(div, btnLeft.nextSibling);
+
+ return div;
+ }
+
+ if (config.loadJsExtensionArgs) {
+ loadJsExtension.apply(undefined, config.loadJsExtensionArgs);
+ }
+
+ window.__diagram = phorge_extension;
+
+});
diff --git a/src/__phutil_library_init__.php b/src/__phutil_library_init__.php
new file mode 100644
--- /dev/null
+++ b/src/__phutil_library_init__.php
@@ -0,0 +1,3 @@
+<?php
+
+phutil_register_library('diagram', __FILE__);
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
new file mode 100644
--- /dev/null
+++ b/src/__phutil_library_map__.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * This file is automatically generated. Use 'arc liberate' to rebuild it
+ *
+ * @generated
+ * @phutil-library-version 2
+ */
+phutil_register_library_map(array(
+ '__library_version__' => 2,
+ 'class' => array(
+ 'Diagram' => 'storage/Diagram.php',
+ 'DiagramApplication' => 'application/DiagramApplication.php',
+ 'DiagramCelerityResources' => 'celerity/DiagramCelerityResources.php',
+ 'DiagramContentTransaction' => 'xaction/DiagramContentTransaction.php',
+ 'DiagramController' => 'controller/DiagramController.php',
+ 'DiagramDAO' => 'storage/DiagramDAO.php',
+ 'DiagramPHIDType' => 'phid/DiagramPHIDType.php',
+ 'DiagramPatchList' => 'storage/patch/DiagramPatchList.php',
+ 'DiagramReplyHandler' => 'mail/DiagramReplyHandler.php',
+ 'DiagramSchemaSpec' => 'storage/DiagramSchemaSpec.php',
+ 'DiagramSearchConduitAPIMethod' => 'conduit/DiagramSearchConduitAPIMethod.php',
+ 'DiagramTransaction' => 'storage/DiagramTransaction.php',
+ 'DiagramTransactionEditor' => 'editor/DiagramTransactionEditor.php',
+ 'DiagramTransactionType' => 'xaction/DiagramTransactionType.php',
+ 'DiagramUploadConduitAPIMethod' => 'conduit/DiagramUploadConduitAPIMethod.php',
+ 'DiagramVersion' => 'storage/DiagramVersion.php',
+ 'PhabricatorDiagramQuery' => 'query/PhabricatorDiagramQuery.php',
+ 'PhabricatorDiagramTransactionQuery' => 'query/PhabricatorDiagramTransactionQuery.php',
+ 'PhabricatorDiagramVersionQuery' => 'query/PhabricatorDiagramVersionQuery.php',
+ 'PhabricatorRemarkupDiagramRule' => 'remarkup/PhabricatorRemarkupDiagramRule.php',
+ 'PlainHtmlWebpageResponse' => 'response/PlainHtmlWebpageResponse.php',
+ ),
+ 'function' => array(),
+ 'xmap' => array(
+ 'Diagram' => array(
+ 'DiagramDAO',
+ 'PhabricatorApplicationTransactionInterface',
+ 'PhabricatorDestructibleInterface',
+ 'PhabricatorPolicyInterface',
+ 'PhabricatorSubscribableInterface',
+ ),
+ 'DiagramApplication' => 'PhabricatorApplication',
+ 'DiagramCelerityResources' => 'CelerityResourcesOnDisk',
+ 'DiagramContentTransaction' => 'DiagramTransactionType',
+ 'DiagramController' => 'PhabricatorController',
+ 'DiagramDAO' => 'PhabricatorLiskDAO',
+ 'DiagramPHIDType' => 'PhabricatorPHIDType',
+ 'DiagramPatchList' => 'PhabricatorSQLPatchList',
+ 'DiagramReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
+ 'DiagramSchemaSpec' => 'PhabricatorConfigSchemaSpec',
+ 'DiagramSearchConduitAPIMethod' => 'ConduitAPIMethod',
+ 'DiagramTransaction' => 'PhabricatorModularTransaction',
+ 'DiagramTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'DiagramTransactionType' => 'PhabricatorModularTransactionType',
+ 'DiagramUploadConduitAPIMethod' => 'ConduitAPIMethod',
+ 'DiagramVersion' => array(
+ 'DiagramDAO',
+ 'PhabricatorDestructibleInterface',
+ 'PhabricatorPolicyInterface',
+ ),
+ 'PhabricatorDiagramQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorDiagramTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'PhabricatorDiagramVersionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorRemarkupDiagramRule' => 'PhabricatorObjectRemarkupRule',
+ 'PlainHtmlWebpageResponse' => 'AphrontHTMLResponse',
+ ),
+));
diff --git a/src/application/DiagramApplication.php b/src/application/DiagramApplication.php
--- a/src/application/DiagramApplication.php
+++ b/src/application/DiagramApplication.php
@@ -35,33 +35,33 @@
// url to image data
"data/(?P<diagramphid>PHID-DGVN-[a-z0-9]{20})"
. "(?:/(?P<version>[^/]+))?"
- . "(?:/|\?|$).*"
+ . "(?:/|\?|$).*"
=> "DiagramController",
-
+
// version info requests
"version/DIAG(?P<versioninfodiagram>(\d+))"
. "/(?P<versioninfopage>(\d+))"
- . "(?:/|\?|$).*"
+ . "(?:/|\?|$).*"
=> "DiagramController",
-
+
// draw.io iframe urls
"(?:(?P<versioneddiagramid>DIAG(\d+))/(?P<version>\d+)/?)"
. "(?P<route>iframe)"
- . "(?:/|\?|$).*"
+ . "(?:/|\?|$).*"
=> "DiagramController",
-
+
// draw.io iframe urls
"(?:(?P<diagramid>DIAG(\d+))/?)?"
. "(?P<route>iframe)"
- . "(?:/|\?|$).*"
+ . "(?:/|\?|$).*"
=> "DiagramController",
-
+
// url with diagram id and version in it
"(?P<versioneddiagramid>DIAG(\d+))"
. "/(?P<version>\d+)"
- . "(?:/|\?|$).*"
+ . "(?:/|\?|$).*"
=> "DiagramController",
-
+
// url with diagram id in it
"(?P<diagramid>DIAG(\d+))"
. "(?:/|\?|$).*"
@@ -72,18 +72,10 @@
. "(?P<subscriptionphid>[^/]+)/"
=> 'DiagramController',
- // url for loading/initializing diagram content
- "(?P<route>loadJsExtension)/"
- . "(/?(?P<loadDiagramName>[^/]*))?"
- . "(/?(?P<loadDiagramPHID>[^/]*))?"
- . "(/?(?P<loadDiagramVersion>[^/]*))?"
- . ".*"
- => 'DiagramController',
-
// other urls
".*"
=> "DiagramController",
)
);
}
-}
\ No newline at end of file
+}
diff --git a/src/celerity/DiagramCelerityResources.php b/src/celerity/DiagramCelerityResources.php
new file mode 100644
--- /dev/null
+++ b/src/celerity/DiagramCelerityResources.php
@@ -0,0 +1,15 @@
+<?php
+
+final class DiagramCelerityResources extends CelerityResourcesOnDisk {
+ public function getName() {
+ return 'diagram-resources';
+ }
+
+ public function getPathToResources() {
+ return phutil_get_library_root('diagram').'/../rsrc';
+ }
+
+ public function getPathToMap() {
+ return phutil_get_library_root('diagram').'/celerity/map.php';
+ }
+}
diff --git a/src/celerity/map.php b/src/celerity/map.php
new file mode 100644
--- /dev/null
+++ b/src/celerity/map.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * This file is automatically generated. Use 'bin/celerity map' to rebuild it.
+ *
+ * @generated
+ */
+return array(
+ 'names' => array(
+ 'iframe-toolbtn.css' => '35ad6f49',
+ 'iframe-toolbtn.js' => '366d7151',
+ 'phorge_extension.js' => 'e0b8cd6d',
+ ),
+ 'symbols' => array(
+ 'diagram-css-iframe-toolbtn' => '35ad6f49',
+ 'diagram-js-iframe-toolbtn' => '366d7151',
+ 'javelin-behavior-diagram-extension' => 'e0b8cd6d',
+ ),
+ 'requires' => array(),
+ 'packages' => array(),
+);
diff --git a/src/controller/DiagramController.php b/src/controller/DiagramController.php
--- a/src/controller/DiagramController.php
+++ b/src/controller/DiagramController.php
@@ -21,9 +21,6 @@
$route = $request->getURIData('route');
$versioninfoDiagramID = $request->getURIData('versioninfodiagram');
$versioninfoPage = $request->getURIData('versioninfopage');
- $loadDiagramName = $request->getURIData('loadDiagramName');
- $loadDiagramPHID = $request->getURIData('loadDiagramPHID');
- $loadDiagramVersion = $request->getURIData('loadDiagramVersion');
if (isset($diagramphid) && !empty(trim($diagramphid))) {
// return PNG image data
@@ -103,39 +100,6 @@
}
}
- if ($route == 'loadJsExtension') {
- $response = new AphrontFileResponse();
- $response->setMimeType('application/javascript');
-
- $base64_data = "";
- if (isset($loadDiagramName) && !empty(trim($loadDiagramName))) {
- $diagram_id = (int) substr($loadDiagramName, strlen("DIAG"));
- if (isset($loadDiagramVersion) && !empty(trim($loadDiagramVersion))) {
- $diagramVersion = id(new DiagramVersion())->loadByVersionedDiagramID(
- $diagram_id,
- $loadDiagramVersion);
- } else {
- $diagramVersion = id(new DiagramVersion())->loadLatestByDiagramID(
- $diagram_id);
- }
- if ($diagramVersion) {
- $data = $diagramVersion->getData();
- $base64_data = base64_encode($data);
- }
- }
-
- $response->setContent('loadJsExtension("'
- . $loadDiagramName
- . '", "'
- . $loadDiagramPHID
- . '", "'
- . $loadDiagramVersion
- . '", "'
- . $base64_data
- . '");');
- return $response;
- }
-
$root = '';
$file = rtrim($request->getPath(), '/');
$root = dirname(phutil_get_library_root('diagram'));
@@ -150,11 +114,11 @@
if ($versioneddiagramid != null && $version != null) {
$file = preg_replace(
"/^\/diagram\/$versioneddiagramid\/$version" ."iframe\//",
- "data/drawio/src/main/webapp/",
+ "drawio/src/main/webapp/",
$file);
} else {
$file = preg_replace("/^\/diagram\/($diagramid\/?)?iframe\//",
- "data/drawio/src/main/webapp/",
+ "drawio/src/main/webapp/",
$file);
}
} else {
@@ -179,20 +143,6 @@
}
}
- // check if we are trying to load "iframe loader" files,
- // if so, correct the path accordingly
- if ($file == "data/drawio/src/main/webapp/index.html") {
- return $this->showIframe($request);
- }
- else
- if ($file == "data/drawio/src/main/webapp/iframe.css"
- || $file == "data/drawio/src/main/webapp/iframe-toolbtn.css"
- || $file == "data/drawio/src/main/webapp/iframe1.js"
- || $file == "data/drawio/src/main/webapp/iframe2.js"
- || $file == "data/drawio/src/main/webapp/iframe-toolbtn.js") {
- $file = str_replace("drawio/src/main/webapp/", "", $file);
- }
-
// determine full path
$path = $root . '/' . $file;
@@ -230,6 +180,7 @@
switch (pathinfo($file, PATHINFO_EXTENSION)) {
case 'html':
$response = id(new PlainHtmlWebpageResponse())
+ ->setDisableContentSecurityPolicy(true)
->setFrameable(true)
->setContent(file_get_contents($path));
break;
@@ -467,10 +418,23 @@
string $diagramName = null,
string $diagramPHID = null,
string $diagramVersion = null,
- string $diagramBase64 = null
+ string $base64_data = ''
) {
$applicationUrl = "/" . explode("/", $request->getPath())[1];
+ $behaviorConfig = array();
+ $behaviorConfig['loadJsExtensionArgs'] = array(
+ $diagramName,
+ $diagramPHID,
+ $diagramVersion,
+ $base64_data
+ );
+ $behaviorConfig['toolbarCss'] = celerity_get_resource_uri('/iframe-toolbtn.css', 'diagram-resources');
+ $behaviorConfig['toolbarJs'] = celerity_get_resource_uri('/iframe-toolbtn.js', 'diagram-resources');
+
+ require_celerity_resource('javelin-behavior');
+ Javelin::initBehavior('diagram-extension', $behaviorConfig, 'diagram-resources');
+
$content = phutil_tag(
'div',
array(),
@@ -489,28 +453,6 @@
'class' => 'drawio',
)),
)),
- phutil_tag(
- 'script',
- array(
- 'src' => 'phorge_extension.js',
- 'defer' => true
- ),
- ''
- ),
- phutil_tag(
- 'script',
- array(
- 'src' => $applicationUrl . '/loadJsExtension/'
- . $diagramName
- . '/'
- . $diagramPHID
- . '/'
- . $diagramVersion
- . '/',
- 'defer' => true
- ),
- ''
- ),
phutil_tag(
'div',
array(
@@ -585,363 +527,4 @@
return $response;
}
- /**
- * Shows the internal draw.io application
- */
- private function showIframe(
- AphrontRequest $request
- ) {
- $content = phutil_tag(
- 'html',
- array(),
- array(
- phutil_tag(
- 'head',
- array(),
- array(
- phutil_tag(
- 'title',
- array(),
- 'Flowchart Maker & Online Diagram Software'
- ),
- phutil_tag(
- 'meta',
- array(
- 'charset' => 'utf-8'
- )
- ),
- phutil_tag(
- 'meta',
- array(
- 'http-equiv' => 'content-type',
- 'content' => 'text/html; charset=utf-8'
- )
- ),
- phutil_tag(
- 'meta',
- array(
- 'name' => 'viewport',
- 'content' => 'width=device-width, initial-scale=1.0, '
- . 'maximum-scale=1.0, user-scalable=no'
- )
- ),
- phutil_tag(
- 'meta',
- array(
- 'name' => 'mobile-web-app-capable',
- 'content' => 'yes'
- )
- ),
- phutil_tag(
- 'link',
- array(
- 'rel' => 'stylesheet',
- 'type' => 'text/css',
- 'href' => 'iframe.css'
- )
- ),
- phutil_tag(
- 'link',
- array(
- 'rel' => 'stylesheet',
- 'type' => 'text/css',
- 'href' => 'iframe-toolbtn.css'
- )
- ),
- phutil_tag(
- 'link',
- array(
- 'rel' => 'stylesheet',
- 'type' => 'text/css',
- 'href' => 'styles/grapheditor.css'
- )
- ),
- phutil_tag(
- 'script',
- array(
- 'type' => 'text/javascript',
- 'src' => 'iframe1.js',
- 'defer' => true
- )
- )
- )
- ),
- phutil_tag(
- 'body',
- array(
- 'class' => 'geEditor'
- ),
- array(
- phutil_tag(
- 'div',
- array(
- 'id' => 'geinfo'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'geBlock'
- ),
- array(
- phutil_tag(
- 'h1',
- array(),
- 'Flowchart Maker and Online Diagram Software'
- ),
- phutil_tag(
- 'p',
- array(),
- 'draw.io is free online diagram software. You can use '
- . 'it as a flowchart maker, network diagram software, '
- . 'to create UML online, as an ER diagram tool, to '
- . 'design database schema, to build BPMN online, as a '
- . 'circuit diagram maker, and more. draw.io can import '
- . '.vsdx, Gliffy™ and Lucidchart™ files.'
- ),
- phutil_tag(
- 'h2',
- array(
- 'id' => 'gestatus'
- ),
- 'Loading...'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'init-spinner'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'content'
- ),
- array(
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(0deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -1s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(27deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.923077ss;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(55deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.846154s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(83deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.769231s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(110deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.692308s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(138deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.615385s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(166deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.538462s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(193deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.461538s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(221deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.384615s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(249deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.307692s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(276deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.230769s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(304deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.153846s;'
- )
- )
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'spike',
- 'style' => 'transform: rotate(332deg)'
- . ' translatex(13px);'
- ),
- phutil_tag(
- 'div',
- array(
- 'class' => 'animator',
- 'style' => 'animation-delay: -0.0769231s;'
- )
- )
- )
- )
- )
- )
- )
- )
- ),
- phutil_tag(
- 'script',
- array(
- 'type' => 'text/javascript',
- 'src' => 'iframe2.js',
- 'defer' => true
- )
- ),
- phutil_tag(
- 'script',
- array(
- 'type' => 'text/javascript',
- 'src' => 'iframe-toolbtn.js',
- 'defer' => true
- )
- )
- )
- )
- )
- );
-
- $response = id(new PlainHtmlWebpageResponse())
- ->setFrameable(true)
- ->setContent($content);
-
- return $response;
- }
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Dec 21, 00:52 (17 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1021557
Default Alt Text
D25577.1734742326.diff (53 KB)
Attached To
Mode
D25577: Makes extension usable with configured alternative file domain
Attached
Detach File
Event Timeline
Log In to Comment