├── firefox
└── .gitignore
├── .gitignore
├── example-reply.png
├── example-tweet.png
├── chrome
├── manifest.json
├── options.js
├── options.html
├── background.js
├── content_script.js
└── jquery-1.7.1.min.js
├── README.md
└── LICENSE
/firefox/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # osx noise
2 | .DS_Store
3 | profile
4 | *.crx
5 | *.pem
--------------------------------------------------------------------------------
/example-reply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpk/IndieWeb-Reply-Browser-Extension/master/example-reply.png
--------------------------------------------------------------------------------
/example-tweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aaronpk/IndieWeb-Reply-Browser-Extension/master/example-tweet.png
--------------------------------------------------------------------------------
/chrome/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "IndieWeb Reply",
3 | "description": "This extension modifies all 'reply' links on Twitter to pop up a 'reply' dialog on your own web page",
4 | "version": "0.0.1",
5 | "options_page": "options.html",
6 | "content_scripts": [
7 | {
8 | "matches": ["http://twitter.com/*", "https://twitter.com/*", "https://twitter.com/", "https://alpha.app.net/*"],
9 | "js": ["jquery-1.7.1.min.js", "content_script.js"]
10 | }
11 | ],
12 | "background": {
13 | "scripts": ["background.js"]
14 | },
15 | "permissions": [
16 | "background", "tabs", "http://*/*", "https://*/*"
17 | ],
18 | "manifest_version": 2
19 | }
20 |
--------------------------------------------------------------------------------
/chrome/options.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 by Aaron Parecki. All rights reserved. Use of this
3 | * source code is governed by a BSD-style license that can be found in the
4 | * LICENSE file.
5 | */
6 |
7 | function saveURL(evt) {
8 | if (window.localStorage == null) {
9 | alert('Local storage is required for changing providers');
10 | return;
11 | }
12 | window.localStorage.IndieWebReplyPostURL = evt.target.value;
13 | }
14 |
15 | function main() {
16 | if (window.localStorage == null) {
17 | alert("LocalStorage must be enabled for changing options.");
18 | return;
19 | }
20 |
21 | document.getElementById('postURL').value = window.localStorage.IndieWebReplyPostURL;
22 | }
23 |
24 | document.addEventListener('DOMContentLoaded', function () {
25 | main();
26 | document.querySelector('#postURL').addEventListener('change', saveURL);
27 | });
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | IndieWeb Reply
2 | ==============
3 |
4 | This browser extension rewrites Twitter.com "reply" buttons to open a browser window to your own site, allowing you to post a reply from your site.
5 |
6 | ## Setup
7 |
8 | Before it can be used, you need to set the URL to redirect to in the IndieWeb Reply extension preferences. This URL is a [web action URL](http://waterpigs.co.uk/articles/web-actions/) which should provide a UI for posting a new note.
9 |
10 | You can use {url} in your URL (perhaps in a query parameter) and it will be replaced by the URL of the tweet you’re replying to.
11 |
12 | ## Usage
13 |
14 | On twitter.com, click "Reply" on a tweet:
15 |
16 | 
17 |
18 | A window opens on your own domain so you can reply from your site
19 |
20 | 
--------------------------------------------------------------------------------
/chrome/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Options for the IndieWeb Reply Extension
5 |
10 |
22 |
23 |
24 |
25 |
Indieweb Reply Preferences
26 |
27 |
28 |
Enter the URL of your new note UI:
29 |
30 |
31 |
32 |
The following expressions will be expanded:
33 |
34 |
35 |
{url}
36 |
Expands to the (urlencoded) URL of the current page
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/chrome/background.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 by Aaron Parecki. All rights reserved. Use of this
3 | * source code is governed by a BSD-style license that can be found in the
4 | * LICENSE file.
5 | */
6 |
7 | chrome.extension.onConnect.addListener(function(port) {
8 | var tab = port.sender.tab;
9 |
10 | console.debug(tab);
11 |
12 | // This will get called by the content script we execute in
13 | // the tab as a result of the user pressing the browser action.
14 | port.onMessage.addListener(function(info) {
15 | console.debug(info);
16 | });
17 | });
18 |
19 | chrome.extension.onMessage.addListener(function (request, sender, sendResponse) {
20 | console.log('onMessage called with request, sender, response:');
21 | console.log(request);
22 | console.log(sender);
23 | console.log(sendResponse);
24 |
25 | if (request.getLocalStorage !== undefined) {
26 | console.log('Getting locally stored variable ' + request.getLocalStorage);
27 |
28 | var response = {};
29 | response[request.getLocalStorage] = window.localStorage[request.getLocalStorage];
30 |
31 | sendResponse(response);
32 | }
33 | });
34 |
35 | chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
36 | console.debug(request);
37 | });
38 |
39 |
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD License
2 | -----------
3 |
4 | Copyright (c) 2012 by Aaron Parecki
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification,
8 | are permitted provided that the following conditions are met:
9 |
10 | 1. Redistributions of source code must retain the above copyright notice,
11 | this list of conditions and the following disclaimer.
12 |
13 | 2. Redistributions in binary form must reproduce the above copyright
14 | notice, this list of conditions and the following disclaimer in the
15 | documentation and/or other materials provided with the distribution.
16 |
17 | 3. Neither the name of Django nor the names of its contributors may be used
18 | to endorse or promote products derived from this software without
19 | specific prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/chrome/content_script.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 by Aaron Parecki. All rights reserved. Use of this
3 | * source code is governed by a BSD-style license that can be found in the
4 | * LICENSE file.
5 | */
6 |
7 | function bindTwitter() {
8 | $("a.js-action-reply").each(function(i,e){
9 | $(e).unbind("click").click(function(evt){
10 | var tweet = $(evt.target).parents(".tweet");
11 | var url = "https://twitter.com/" + $(tweet).data('screen-name') + "/status/" + $(tweet).attr('data-item-id');
12 |
13 | var replace = {
14 | url: encodeURI(url)
15 | };
16 |
17 | chrome.extension.sendMessage({'getLocalStorage': 'IndieWebReplyPostURL'}, function (response) {
18 | var postURL = response.IndieWebReplyPostURL;
19 |
20 | if (postURL === undefined) {
21 | alert('You must set a Reply Post URL in Chrome options in order to use IndieWeb Reply');
22 | return false;
23 | }
24 |
25 | // Replace template vars
26 | for (var template in replace) {
27 | if (replace[template] == undefined)
28 | continue;
29 | postURL = postURL.split('{' + template + '}').join(replace[template]);
30 | }
31 |
32 | window.open(postURL);
33 | });
34 |
35 | return false;
36 | });
37 | });
38 | }
39 |
40 | bindTwitter();
41 | setTimeout(bindTwitter, 5000);
42 |
43 | $("a[data-reply-to='']").each(function(i,e){
44 | $(e).click(function(evt){
45 | var post = $(evt.target).parents(".post-container");
46 | var url = "https://alpha.app.net/" + $(post).data('post-author-username') + "/post/" + $(post).attr('data-post-id');
47 | var postURL = "http://pk.dev/admin/?reply_to=" + encodeURI(url);
48 | window.open(postURL);
49 | return false;
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/chrome/jquery-1.7.1.min.js:
--------------------------------------------------------------------------------
1 | /*! jQuery v1.7.1 jquery.com | jquery.org/license */
2 | (function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="