diff --git a/README.md b/README.md index cb277ee..f9b4051 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ Check link stats by adding **+** to the URL. Example [pygy.co/pygmy+](https://py - [How Link Stats Are Generated?](#how-link-stats-are-generated) - [How Pygmy Auth Token Works?](#how-pygmy-auth-token-works) - [Development](#development) +- [Browser extension](#browser-extension) + - [Development of the extension](#development-of-the-extension) - [Contributions](#contributions) - [Sponsorship](#sponsorship) - [License](#license) @@ -247,10 +249,24 @@ See coverage report(Coverage is bad because the coverage for integration tests i `coverage report` +Browser extension +================= + +Chrome extension is available on Chrome Store with the name: [your ShortURL service (pygmy)](https://chrome.google.com/webstore/detail/your-shorturl-service-pyg/kchefoihlnhfcaphckpopdknaolfegkp). + +Development of the extension +---------------------------- + +* Enable development switch button on Crhome extension manager section +* Load extension from a folder +* Now you can start evolving the code and reload it with just one click on the Chrome extensions + + Contributions ============= -Thanks [batarian71](https://github.com/batarian71) for providing the logo icon. +* Thanks [batarian71](https://github.com/batarian71) for providing the logo icon. +* [oriolrius](http://oriolrius.cat) Chrome extension. Sponsorship =========== diff --git a/extensions/chrome/clipboard.min.js b/extensions/chrome/clipboard.min.js new file mode 100644 index 0000000..b00ee51 --- /dev/null +++ b/extensions/chrome/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.0 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=3)}([function(t,e,n){var o,r,i;!function(a,c){r=[t,n(7)],o=c,void 0!==(i="function"==typeof o?o.apply(e,r):o)&&(t.exports=i)}(0,function(t,e){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(t){return t&&t.__esModule?t:{default:t}}(e),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,o.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,o.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=a})},function(t,e,n){function o(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!c.string(e))throw new TypeError("Second argument must be a String");if(!c.fn(n))throw new TypeError("Third argument must be a Function");if(c.node(t))return r(t,e,n);if(c.nodeList(t))return i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function r(t,e,n){return t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function i(t,e,n){return Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function a(t,e,n){return u(document.body,t,e,n)}var c=n(6),u=n(5);t.exports=o},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function o(){r.off(t,o),e.apply(n,arguments)}var r=this;return o._=e,this.on(t,o,n)},emit:function(t){var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;for(o;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,f.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(s.default);t.exports=p})},function(t,e){function n(t,e){for(;t&&t.nodeType!==o;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}var o=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var r=Element.prototype;r.matches=r.matchesSelector||r.mozMatchesSelector||r.msMatchesSelector||r.oMatchesSelector||r.webkitMatchesSelector}t.exports=n},function(t,e,n){function o(t,e,n,o,r){var a=i.apply(this,arguments);return t.addEventListener(n,a,r),{destroy:function(){t.removeEventListener(n,a,r)}}}function r(t,e,n,r,i){return"function"==typeof t.addEventListener?o.apply(null,arguments):"function"==typeof n?o.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return o(t,e,n,r,i)}))}function i(t,e,n,o){return function(n){n.delegateTarget=a(n.target,e),n.delegateTarget&&o.call(t,n)}}var a=n(4);t.exports=r},function(t,e){e.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},e.nodeList=function(t){var n=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in t&&(0===t.length||e.node(t[0]))},e.string=function(t){return"string"==typeof t||t instanceof String},e.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e){function n(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}t.exports=n}])}); \ No newline at end of file diff --git a/extensions/chrome/icon.png b/extensions/chrome/icon.png new file mode 100644 index 0000000..127a768 Binary files /dev/null and b/extensions/chrome/icon.png differ diff --git a/extensions/chrome/manifest.json b/extensions/chrome/manifest.json new file mode 100644 index 0000000..3f8fce3 --- /dev/null +++ b/extensions/chrome/manifest.json @@ -0,0 +1,24 @@ +{ + "manifest_version": 2, + "name": "your ShortURL service (pygmy)", + "version": "0.4", + "description": "pygmy shorturl Chrome extension", + "icons": { + "16": "icon.png" + }, + "browser_action": { + "default_icon": "icon.png", + "default_title": "Create short URLs", + "default_popup": "popup.html" + }, + "options_ui": { + "page": "options.html", + "open_in_tab": false + }, + "permissions": [ + "tabs", + "clipboardWrite", + "activeTab", + "storage" + ] +} \ No newline at end of file diff --git a/extensions/chrome/options.html b/extensions/chrome/options.html new file mode 100644 index 0000000..ecc055e --- /dev/null +++ b/extensions/chrome/options.html @@ -0,0 +1,17 @@ + + + + pymyg shorturl settings + + +
+
+ +
+
+ +
+
+ + + \ No newline at end of file diff --git a/extensions/chrome/options.js b/extensions/chrome/options.js new file mode 100644 index 0000000..54d299c --- /dev/null +++ b/extensions/chrome/options.js @@ -0,0 +1,29 @@ +// Saves settings to chrome.storage +function save_options() { + var rest_url = document.getElementById('rest_url').value; + chrome.storage.sync.set({ 'restUrl': rest_url}, + function() { + console.log('saved'); + // Update status to let user know options were saved. + var status = document.getElementById('status'); + status.innerHTML = '
Settings saved.

'; + setTimeout(function() { + status.innerHTML = ''; + }, 1500); + } + ); +} + +// Restore settings from chrome.storage +function restore_options() { + // Set default + chrome.storage.sync.get({ restUrl: 'https://api.url.joor.net'}, + function(items) { + document.getElementById('rest_url').value = items.restUrl; + } + ); +} + +// linking events to the previous functions +document.addEventListener('DOMContentLoaded', restore_options); +document.getElementById('save').addEventListener('click', save_options); \ No newline at end of file diff --git a/extensions/chrome/popup.html b/extensions/chrome/popup.html new file mode 100644 index 0000000..4418535 --- /dev/null +++ b/extensions/chrome/popup.html @@ -0,0 +1,23 @@ + + + + ShortURL + + + + +
+
+ +
+

+ +
--- not defined ---

+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/extensions/chrome/popup.js b/extensions/chrome/popup.js new file mode 100644 index 0000000..32ca7ce --- /dev/null +++ b/extensions/chrome/popup.js @@ -0,0 +1,65 @@ +var su = 'default value'; + +function makeShort(tablink) { + chrome.storage.sync.get({ restUrl: 'https://api.url.joor.net'}, + function(items) { + var rest_url = items.restUrl; + document.getElementById("backend").innerHTML = '\ + Backend used: '+rest_url+'
\ + Change the backend using extension options.'; + rest_query( tablink, rest_url); + } + ); +} + +// REST client query +function rest_query(tablink, rest_url) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", rest_url + '/api/shorten', true); + xhr.setRequestHeader("Content-Type", "application/json"); + var req = {name:"John Rambo", time:"2pm"}; + var req = { + secret_key: "", + expire_after: null, + is_protected: false, + owner: null, + is_custom: false, + short_code: "", + long_url: tablink, + description: null + }; + var data = JSON.stringify(req); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + var jresponse = JSON.parse(xhr.responseText); + su = jresponse.short_url; //+ '/' + jresponse.short_code; + var link = ""+su+""; + document.getElementById("short-url").innerHTML = link; + document.getElementById("short-url").addEventListener('click', function() { + chrome.tabs.create({ url: su}); + }) + } + } + xhr.send(data); +} + +chrome.tabs.getSelected(null,function(tab) { + var tablink = tab.url; + document.getElementById("long-url").setAttribute('value',tablink); + document.getElementById("short").onclick = function() { + makeShort(tablink); + }; + + var cc = new ClipboardJS('#cc'); + cc.on('success', function(e) { + document.getElementById('status').innerHTML = '
Copied to clipboard.

'; + setTimeout(function() { + document.getElementById('status').innerHTML = ''; + }, 1500); + }); + cc.on('error', function(e) { + console.error('Action:', e.action); + console.error('Trigger:', e.trigger); + }); +}); +