├── README
├── chrome.manifest
├── content
├── browser.js
├── browser.xul
├── firstrun.js
├── media
│ ├── by.png
│ ├── cc-plus.png
│ ├── cc.png
│ ├── cc.small.png
│ ├── nc.png
│ ├── nd.png
│ ├── pd.png
│ ├── remix.png
│ ├── sa.png
│ ├── share.png
│ └── zero.png
├── pageInfo.js
└── pageInfo.xul
├── defaults
└── preferences
│ └── preferences.js
├── doc
├── FUNCSPEC
├── WEBSITES
└── proposal.pdf
├── icon.png
├── install.rdf
├── locale
├── en-US
│ └── locale.properties
└── ru-RU
│ └── locale.properties
├── module
├── bootstrap.js
├── ccffext.js
├── hacks
│ └── hacks.js
├── license.js
└── rdfa.js
├── skin
├── browser.css
├── icon.svg
├── icon32.png
├── icon64.png
└── pageInfo.css
└── xpi.sh
/README:
--------------------------------------------------------------------------------
1 | OpenAttribute for Firefox
2 | =========================
3 |
4 | OpenAttribute for Firefox adds support for displaying license and
5 | attribution information for works as you browse. If the page or items
6 | on the page are licensed, a CC icon will appear in the URL bar, which
7 | can be clicked to display the author and license information. This
8 | metadata is used to generate HTML which can be used to attribute
9 | reuse.
10 |
11 | This add-on is the successor to MozCC
12 | (http://wiki.creativecommons.org/MozCC) and was initially developed
13 | with support from Google Summer of Code.
14 |
--------------------------------------------------------------------------------
/chrome.manifest:
--------------------------------------------------------------------------------
1 | # The "ccffext" is chosen to be the internal identifier of the extension
2 |
3 | # Main data
4 | content ccffext content/
5 |
6 | # Localizations
7 | # For now, only the "en-US" locale is supported
8 | locale ccffext en-US locale/en-US/
9 |
10 | # Skins (style definitions)
11 | skin ccffext classic/1.0 skin/
12 |
13 | # Code modules
14 | resource ccffext module/
15 |
16 | # Changes to the UI: the main window is altered, as well as the "Page Info" frame
17 | overlay chrome://browser/content/browser.xul chrome://ccffext/content/browser.xul
18 | overlay chrome://browser/content/pageinfo/pageInfo.xul chrome://ccffext/content/pageInfo.xul
19 |
--------------------------------------------------------------------------------
/content/browser.js:
--------------------------------------------------------------------------------
1 | var gCcHandler = {
2 |
3 | // Smart Getters
4 | get _icon () {
5 | return document.getElementById('ccffext-icon');
6 | },
7 |
8 | get _popup () {
9 | return document.getElementById('ccffext-popup');
10 | },
11 |
12 | get _popup_work_title () {
13 | return document.getElementById('ccffext-popup-work-title');
14 | },
15 |
16 | get _popup_attribution () {
17 | return document.getElementById('ccffext-popup-attribution-link');
18 | },
19 |
20 | get _popup_license () {
21 | return document.getElementById('ccffext-popup-license-link');
22 | },
23 |
24 | get _popup_license_band () {
25 | return document.getElementById('ccffext-popup-license-band');
26 | },
27 |
28 | get _popup_num_licensed_objects () {
29 | return document.getElementById('ccffext-popup-licensed-objects');
30 | },
31 |
32 | get _license_browser () {
33 | return document.getElementById('ccffext-license-frame');
34 | },
35 |
36 | resetPopup : function() {
37 | // hide popup elements which may or may not be shown for this page
38 | this._popup_license.hidden = true;
39 | this._popup_work_title.hidden = true;
40 | this._popup_num_licensed_objects.hidden = true;
41 | this._popup_attribution.hidden = true;
42 | this._popup_license_band.setAttribute(
43 | "class", "band-reset");
44 | },
45 |
46 | // Popup Handlers
47 | handleIconClick : function(e) {
48 |
49 | this.resetPopup();
50 |
51 | // update the popup with the license information
52 | var doc_subject = {uri:content.document.location.href};
53 |
54 | // -- license
55 | var license = ccffext.objects.getLicense(
56 | content.document.location.href, doc_subject);
57 | var is_doc_licensed = false;
58 |
59 | if ("undefined" != typeof license) {
60 | // document is licensed
61 | is_doc_licensed = true;
62 |
63 | this._popup_license.hidden = false;
64 | this._popup_license.value = license.uri;
65 | this._popup_license.setAttribute('href', license.uri);
66 |
67 | // ---- get the license details and update the popup when ready
68 | licenses.getLicenseInfo(
69 | ccffext.objects.getLicense(content.document.location.href,
70 | doc_subject).uri,
71 | function(license) {
72 | gCcHandler._popup_license.value = license.name;
73 | gCcHandler._popup_license_band.setAttribute(
74 | "class", "band-" + license.color);
75 | }, []);
76 |
77 | // -- title
78 | this._popup_work_title.hidden = false;
79 | this._popup_work_title.value = ccffext.objects.getDisplayTitle(
80 | content.document.location.href, doc_subject);
81 |
82 | // -- attribution link
83 | let author = ccffext.objects.getAuthor(
84 | content.document.location.href, doc_subject);
85 | let author_uri = ccffext.objects.getAuthorUri(
86 | content.document.location.href, doc_subject);
87 |
88 | if ("undefined" != typeof author ||
89 | "undefined" != typeof author_uri) {
90 |
91 | // at least one has been provided
92 | this._popup_attribution.hidden = false;
93 |
94 | if ("undefined" == typeof author &&
95 | "undefined" != typeof author_uri)
96 | author = author_uri;
97 |
98 | if ("undefined" != typeof author) {
99 | // attribution name was supplied
100 | this._popup_attribution.value = author;
101 | }
102 |
103 | if ("undefined" != typeof author_uri) {
104 | this._popup_attribution.setAttribute('href',
105 | author_uri.uri);
106 | this._popup_attribution.setAttribute(
107 | "class", "text-link");
108 | } else {
109 | // no attribution URL
110 | this._popup_attribution.setAttribute(
111 | "class", "");
112 | }
113 | }
114 |
115 | } // if license is not undefined
116 |
117 | // how many licensed objects described by this page, excluding the page
118 | var count = ccffext.objects.getLicensedSubjects(
119 | content.document.location.href).length - (is_doc_licensed?1:0);
120 | if (count > 0) {
121 | this._popup_num_licensed_objects.value =
122 | ccffext.l10n.get("icon.title.label", count);
123 | this._popup_num_licensed_objects.hidden = false;
124 | }
125 |
126 | // show the popup
127 | this._popup.hidden = false;
128 |
129 | var position = (getComputedStyle(gNavToolbox, "").direction == "rtl") ? 'after_end' : 'after_start';
130 |
131 | this._popup.openPopup(this._icon, position);
132 | },
133 |
134 | handleMoreInfo : function(e) {
135 | gCcHandler.hidePopup();
136 | BrowserPageInfo(null,'ccffext-tab');
137 | },
138 |
139 | handleCopyHtml : function(e) {
140 | const clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"].
141 | getService(Components.interfaces.nsIClipboardHelper);
142 | clipboard.copyString(
143 | ccffext.objects.getAttributionHtml(
144 | content.document.location.href,
145 | {'uri':content.document.location.href}));
146 |
147 | // close the popup
148 | gCcHandler.hidePopup();
149 | },
150 |
151 | handleCopyText : function(e) {
152 | const clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"].
153 | getService(Components.interfaces.nsIClipboardHelper);
154 | clipboard.copyString(
155 | ccffext.objects.getAttributionText(
156 | content.document.location.href,
157 | {'uri':content.document.location.href}));
158 |
159 | // close the popup
160 | gCcHandler.hidePopup();
161 | },
162 |
163 | hidePopup : function() {
164 | document.getElementById('ccffext-popup').hidePopup();
165 | },
166 |
167 | // URL Bar manipulators
168 | hideIcon : function() {
169 | this._icon.hidden = true;
170 | },
171 |
172 | showIcon : function(document) {
173 | const objects = ccffext.objects.getLicensedSubjects(
174 | document.location.href);
175 |
176 | this._icon.hidden = false;
177 | gCcHandler._icon.setAttribute("tooltiptext",
178 | ccffext.l10n.get("icon.title.label",
179 | objects.length));
180 | },
181 |
182 | showIconIfLicenseInfo : function(document) {
183 |
184 | // if no document is provided, default to the active document
185 | if ("undefined" == typeof document) {
186 | document = gBrowser.contentDocument;
187 | }
188 |
189 | // if this is the active document, hide the icon
190 | if (gBrowser.contentDocument == document)
191 | gCcHandler.hideIcon();
192 |
193 | if (document instanceof HTMLDocument) {
194 | ccffext.objects.callbackify(
195 | document,
196 | function(document,objects) {
197 | if (gBrowser.contentDocument == document)
198 | gCcHandler.showIcon(document);
199 | },
200 | function(document) {
201 | // license not cached
202 | ccffext.objects.parse(document.location.href, document);
203 | });
204 | }
205 | }
206 | };
207 |
208 | /**
209 | * Register window load listener which adds event listeners for tab,
210 | * location, and state changes.
211 | **/
212 | window.addEventListener("load",function() {
213 |
214 | gBrowser.addEventListener(
215 | "TabSelect",
216 | function(e) { gCcHandler.showIconIfLicenseInfo();}, false);
217 | gBrowser.tabContainer.addEventListener(
218 | "TabSelect",
219 | function(e) { gCcHandler.showIconIfLicenseInfo();}, false);
220 |
221 | gBrowser.addTabsProgressListener({
222 | onLocationChange : function(browser, progress,request,uri) {
223 |
224 | // A tab is opened, closed, or switched to
225 | // Show the location bar icon if license information is present
226 |
227 | // XXX disabling; this is called before the browser fully
228 | // -- loads the document, causing errors in the parse process
229 | // gCcHandler.showIconIfLicenseInfo(progress.DOMWindow.document);
230 |
231 | },
232 |
233 | onStateChange : function(browser, progress,request,flag,status) {
234 |
235 | // A document in an existing tab stopped loading
236 | if (flag & Components.interfaces.nsIWebProgressListener.STATE_STOP)
237 | {
238 | const doc = progress.DOMWindow.document;
239 |
240 | gCcHandler.showIconIfLicenseInfo(progress.DOMWindow.document);
241 | }
242 | },
243 |
244 | });
245 |
246 | licenses.init(gCcHandler._license_browser);
247 |
248 | },false);
249 |
--------------------------------------------------------------------------------
/content/browser.xul:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
13 |
14 |
15 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/content/firstrun.js:
--------------------------------------------------------------------------------
1 | var Prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
2 | Prefs = Prefs.getBranch("extensions.ccffext.");
3 |
4 | var Overlay = {
5 |
6 | init: function(){
7 |
8 |
9 | var ver = -1, firstrun = true;
10 |
11 | try{
12 | firstrun = Prefs.getBoolPref("firstrun");
13 | }catch(e){
14 | //nothing
15 | }finally{
16 |
17 | if (firstrun) {
18 |
19 | window.setTimeout(function(){
20 | var nb = gBrowser.getNotificationBox();
21 |
22 | var buttons = [{
23 | 'label':'More information',
24 | 'accessKey':'I',
25 | callback: function(n, btn) {
26 | nb.removeTransientNotifications();
27 |
28 | gBrowser.selectedTab = gBrowser.addTab("http://openattribute.com/first-run-firefox/");
29 | return true;
30 | }
31 | }];
32 |
33 | nb.appendNotification(
34 | "You've installed OpenAttribute, an add-on that helps you find CC licensed works and properly attribute them.",
35 | 'installed-oa',
36 | 'chrome://ccffext/skin/icon32.png',
37 | nb.PRIORITY_INFO_LOW,
38 | buttons);
39 |
40 | }, 2000);
41 |
42 | Prefs.setBoolPref("firstrun",false);
43 | }
44 | }
45 |
46 | window.removeEventListener("load",Overlay.init,false);
47 | }
48 |
49 | };
50 |
51 | window.addEventListener("load",Overlay.init,false);
52 |
--------------------------------------------------------------------------------
/content/media/by.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/by.png
--------------------------------------------------------------------------------
/content/media/cc-plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/cc-plus.png
--------------------------------------------------------------------------------
/content/media/cc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/cc.png
--------------------------------------------------------------------------------
/content/media/cc.small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/cc.small.png
--------------------------------------------------------------------------------
/content/media/nc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/nc.png
--------------------------------------------------------------------------------
/content/media/nd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/nd.png
--------------------------------------------------------------------------------
/content/media/pd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/pd.png
--------------------------------------------------------------------------------
/content/media/remix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/remix.png
--------------------------------------------------------------------------------
/content/media/sa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/sa.png
--------------------------------------------------------------------------------
/content/media/share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/share.png
--------------------------------------------------------------------------------
/content/media/zero.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/content/media/zero.png
--------------------------------------------------------------------------------
/content/pageInfo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Create a panel for a tab at the top
3 | **/
4 | {
5 | // Main outer panel
6 | const panel = document.createElement("vbox");
7 | panel.setAttribute("id","ccffextPanel");
8 | document.getElementById("mainDeck").appendChild(panel);
9 |
10 | // Horizontal box at the top
11 | const topLine = document.createElement("hbox");
12 | panel.appendChild(topLine);
13 |
14 | // Label showing the number of licensed objects
15 | const numberLabel = document.createElement("label");
16 | numberLabel.setAttribute("flex","1");
17 | topLine.appendChild(numberLabel);
18 |
19 | // Checkbox indicating whether to highlight licensed objects on page or not
20 | const highlightBox = document.createElement("checkbox");
21 | highlightBox.setAttribute("id","ccffext-highlight");
22 | highlightBox.setAttribute("label",ccffext.l10n.get("checkbox.highlight.label"));
23 | highlightBox.setAttribute("checked","true"); // By default, the objects are highlighted
24 | // topLine.appendChild(highlightBox);
25 |
26 | // List of licensed objects
27 | const list = document.createElement("vbox");
28 | list.setAttribute("id","ccffext-objects");
29 | list.setAttribute("flex","1");
30 | panel.appendChild(list);
31 |
32 | // List of added items
33 | var items = [];
34 | }
35 |
36 | /**
37 | * Add a tab at the top
38 | **/
39 | {
40 | const tab = document.createElement("radio");
41 |
42 | with (tab)
43 | {
44 | setAttribute("id","ccffext-tab");
45 | setAttribute("label",ccffext.l10n.get("tab.title.label"));
46 | setAttribute("accesskey",ccffext.l10n.get("tab.title.key"));
47 |
48 | addEventListener("command",function() {
49 |
50 | const doc = window.opener.content.document;
51 | const objects = ccffext.objects.getLicensedSubjects(
52 | doc.location.href);
53 |
54 | // Update the label showing the number of objects
55 | numberLabel.setAttribute("value",
56 | ccffext.l10n.get("label.number.label",objects.length));
57 |
58 | // Remove all previously added items from the list
59 | for (let i = 0; i < items.length; ++i)
60 | {
61 | list.removeChild(items[i]);
62 | }
63 |
64 | items = [];
65 |
66 | var convertStrings = function(strings,part)
67 | {
68 | var converted = [];
69 |
70 | for (let i = 0; i < strings.length; ++i)
71 | {
72 | let name = strings[i].toLowerCase().replace(/[^a-z]/g,"");
73 | converted.push(ccffext.l10n.get("object.license." + part + "." + name + ".label"));
74 | }
75 |
76 | var line = converted.join(", ");
77 | return line.substring(0,1).toUpperCase() + line.substring(1);
78 | }
79 |
80 | // Add new items to the list
81 | for (let i = 0; i < objects.length; ++i)
82 | {
83 | const item = document.createElement("hbox");
84 | item.setAttribute("class","item");
85 | items.push(item);
86 | list.appendChild(item);
87 |
88 | const leftPanel = document.createElement("vbox");
89 | leftPanel.setAttribute("class","primary");
90 | leftPanel.setAttribute("flex","1");
91 | item.appendChild(leftPanel);
92 |
93 | const title = document.createElement("label");
94 | title.setAttribute("class","title");
95 | title.setAttribute("value",ccffext.objects.getDisplayTitle(
96 | doc.location.href, objects[i]));
97 | leftPanel.appendChild(title);
98 |
99 | const sourceLine = document.createElement("hbox");
100 | sourceLine.setAttribute("class","line primary");
101 | leftPanel.appendChild(sourceLine);
102 |
103 | const sourceTitle = document.createElement("label");
104 | sourceTitle.setAttribute("class","line-title");
105 | sourceTitle.setAttribute("value",ccffext.l10n.get("object.source.title.label"));
106 | sourceLine.appendChild(sourceTitle);
107 |
108 | var source = ccffext.objects.getSource(
109 | doc.location.href, objects[i]);
110 | const sourceValue = document.createElement("label");
111 | sourceValue.setAttribute("class","anchor");
112 | sourceValue.setAttribute("value",source);
113 | sourceValue.setAttribute("uri",source);
114 | sourceValue.addEventListener("click",function() {
115 | window.open(this.getAttribute("uri"));
116 | },true);
117 | sourceLine.appendChild(sourceValue);
118 |
119 | var type = ccffext.objects.getType(
120 | doc.location.href, objects[i]);
121 | if ("undefined" != typeof type)
122 | {
123 | const typeLine = document.createElement("hbox");
124 | typeLine.setAttribute("class","line");
125 | leftPanel.appendChild(typeLine);
126 |
127 | const typeTitle = document.createElement("label");
128 | typeTitle.setAttribute("class","line-title");
129 | typeTitle.setAttribute("value",ccffext.l10n.get("object.type.title.label"));
130 | typeLine.appendChild(typeTitle);
131 |
132 | const typeValue = document.createElement("label");
133 | var line = ccffext.l10n.get("object.type."
134 | + type.toLowerCase().replace(/[^a-z]/g,"") + ".label");
135 | typeValue.setAttribute("value",line.substring(0,1).toUpperCase() + line.substring(1));
136 | typeLine.appendChild(typeValue);
137 | }
138 |
139 | var author = ccffext.objects.getAuthor(
140 | doc.location.href, objects[i]);
141 | var authorUri = ccffext.objects.getAuthorUri(
142 | doc.location.href, objects[i]);
143 | if ("undefined" != typeof author)
144 | {
145 | const authorLine = document.createElement("hbox");
146 | authorLine.setAttribute("class","line");
147 | leftPanel.appendChild(authorLine);
148 |
149 | const authorTitle = document.createElement("label");
150 | authorTitle.setAttribute("class","line-title");
151 | authorTitle.setAttribute("value",ccffext.l10n.get("object.author.title.label"));
152 | authorLine.appendChild(authorTitle);
153 |
154 | const authorValue = document.createElement("label");
155 | authorValue.setAttribute("value",author);
156 | authorLine.appendChild(authorValue);
157 |
158 | if ("undefined" != authorUri)
159 | {
160 | authorValue.setAttribute("class","anchor");
161 | authorValue.setAttribute("uri",authorUri.uri);
162 | authorValue.addEventListener("click",function(event) {
163 | window.open(this.getAttribute("uri"));
164 | },true);
165 | }
166 | }
167 |
168 | const licenseLine = document.createElement("hbox");
169 | licenseLine.setAttribute("class","line primary");
170 | leftPanel.appendChild(licenseLine);
171 |
172 | const licenseTitle = document.createElement("label");
173 | licenseTitle.setAttribute("class","line-title");
174 | licenseTitle.setAttribute("value",ccffext.l10n.get("object.license.title.label"));
175 | licenseLine.appendChild(licenseTitle);
176 |
177 | var licenseValue = document.createElement("label");
178 | licenseValue.setAttribute("class","anchor");
179 | licenseValue.addEventListener("click",function(event) {
180 | window.open(this.getAttribute("uri"));
181 | },true);
182 | licenseLine.appendChild(licenseValue);
183 |
184 | const attribLine = document.createElement("vbox");
185 | attribLine.setAttribute("class","line primary");
186 | leftPanel.appendChild(attribLine);
187 |
188 | const attribTitleContainer = document.createElement("hbox");
189 | attribTitleContainer.setAttribute("class", "aligncenter");
190 | attribLine.appendChild(attribTitleContainer);
191 |
192 | const attribTitle = document.createElement("label");
193 | attribTitle.setAttribute("class","line-title");
194 | attribTitle.setAttribute("value",
195 | ccffext.l10n.get("object.attribution.label"));
196 | attribTitleContainer.appendChild(attribTitle);
197 |
198 | const attribFormat = document.createElement("menulist");
199 | const attribFormatMenu = document.createElement("menupopup");
200 | const attribFormatHtml = document.createElement("menuitem");
201 | attribFormatHtml.setAttribute("label", "HTML");
202 | attribFormatHtml.setAttribute("value", "html");
203 | attribFormatMenu.appendChild(attribFormatHtml);
204 |
205 | const attribFormatText = document.createElement("menuitem");
206 | attribFormatText.setAttribute("label", "Plain Text");
207 | attribFormatText.setAttribute("value", "text");
208 | attribFormatMenu.appendChild(attribFormatText);
209 |
210 | attribFormat.appendChild(attribFormatMenu);
211 |
212 | attribTitleContainer.appendChild(attribFormat);
213 |
214 | const attribContainer = document.createElement("hbox");
215 | attribContainer.setAttribute("class", "indented");
216 | attribContainer.setAttribute("align", "start");
217 | attribLine.appendChild(attribContainer);
218 |
219 | var attribText = document.createElement("textbox");
220 | attribText.setAttribute("flex","1");
221 | attribText.setAttribute("multiline", "true");
222 | attribText.setAttribute("readonly", "true");
223 | attribText.addEventListener("focus", function(e) {
224 | e.currentTarget.select();
225 | }, true);
226 | attribContainer.appendChild(attribText);
227 |
228 | function formatListener(textbox, doc_uri, object) {
229 | function onSelect(e) {
230 | switch (e.currentTarget.selectedItem.value) {
231 | case "html":
232 | textbox.setAttribute(
233 | "value",
234 | ccffext.objects.getAttributionHtml(
235 | doc_uri, object));
236 | break;
237 | case "text":
238 | textbox.setAttribute(
239 | "value",
240 | ccffext.objects.getAttributionText(
241 | doc_uri, object));
242 | break;
243 | }
244 | }
245 |
246 | return onSelect;
247 | }
248 |
249 | attribFormat.addEventListener(
250 | "select",
251 | formatListener(attribText, doc.location.href, objects[i]),
252 | true);
253 |
254 | licenses.getLicenseInfo(
255 | ccffext.objects.getLicense(doc.location.href,
256 | objects[i]).uri,
257 | function(license, args) {
258 | args[0].setAttribute("value",license.name);
259 | args[0].setAttribute("uri",license.uri);
260 | args[1].setAttribute(
261 | "value",
262 | ccffext.objects.getAttributionHtml(args[2], args[3]));
263 | }, [licenseValue, attribText, doc.location.href,
264 | objects[i]]);
265 |
266 | const attribCopyButton = document.createElement("button");
267 | attribCopyButton.setAttribute("label",
268 | ccffext.l10n.get("copy"));
269 | attribCopyButton.setAttribute("accesskey",
270 | ccffext.l10n.get("object.button.attributionashtml.key"));
271 | attribCopyButton.addEventListener(
272 | "click",
273 | function(textbox) {
274 | function clickHandler(event) {
275 | const clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"].
276 | getService(Components.interfaces.nsIClipboardHelper);
277 | clipboard.copyString(textbox.getAttribute("value"));
278 | }
279 | return clickHandler;
280 | }(attribText), true);
281 |
282 | attribContainer.appendChild(attribCopyButton);
283 | }
284 |
285 | // Eventually switch to the tab
286 | showTab('ccffext'); // Activates a panel with the "ccffextPanel" id
287 | },true);
288 | }
289 |
290 | document.getElementById("viewGroup").appendChild(tab);
291 | }
292 |
--------------------------------------------------------------------------------
/content/pageInfo.xul:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
11 |
12 |
13 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/defaults/preferences/preferences.js:
--------------------------------------------------------------------------------
1 | pref("extensions.ccffext.firstrun", true);
2 |
--------------------------------------------------------------------------------
/doc/FUNCSPEC:
--------------------------------------------------------------------------------
1 | Functional and UI specification
2 | -------------------------------
3 |
4 | Any software is made to provide ways to solve a specific problem of its end user. The "end user" is not straightly meant to be a person, but in most cases she is. So, in case of application software, the specification of functionality may be done by listing the ways the software may solve its user`s problems. In case of a browser extension, these ways are cnnected to the UI the user sees. So, lets summarize what UI elements the extension should provide:
5 |
6 | + The icon placed in the location bar
7 | + The icon image should refer to CC and reflect the extension`s purpose
8 | + The icon should only be displayed if there`re any licensed objects
9 | + The icon should be clickable, providing extended information about objects
10 | + The icon should have a tooltip, indicating the number of licensed objects
11 | - The icon should have a context menu (be right-clickable)
12 | - The context menu should provide a way to navigate to extended information about licensed objects
13 | - The context menu should provide a way to highlight licensed objects
14 | + The tab in the "Page Info" dialog
15 | + The tab image should also refer to CC and reflect the extension`s purpose
16 | ± There should be a checkbox to turn the highlighting on/off
17 | + There should be a label showing the number of all licensed objects
18 | + There should be a list displaying an entry for any of licensed objects
19 | + The name of the object should be displayed
20 | ± Some information about the object should be shown:
21 | + The type of the object
22 | + The information about object`s creator
23 | - The information about object`s rights holder
24 | + The hyperlink to the soure
25 | ± Some licensing information should be shown:
26 | + The name of the license
27 | - Standard СС icon(s)
28 | + Hyperlink to the extended information about the license
29 | + The summary of permissions, requirements and prohibitions
30 | + The way to obtain the attribution information (a context menu-enabled button)
31 | - Licensed objects` highlighting
32 |
33 | Here "+" marks already implemented features, "±" — partly implemented, "-" — unimplemented. This list is the subject for updates during the development period.
34 |
35 | On the other hand, there`re the other criteria to be used for functional specification, primarily for technical specialists who`re more familiar with the domain. Due to the fact that the extension is the interface between markup languages used to bring web-pages (or, more specifically, embedded RDFa markup) and a human being, the criterion may be the following: list the elements in the markup that are perceived by the extension.
36 |
37 | The extension may use information defined by:
38 | + http://www.w3.org/1999/xhtml/vocab#
39 | + copyright
40 | + license
41 | + http://creativecommons.org/ns#
42 | + Reproduction
43 | + Distribution
44 | + DerivativeWorks
45 | + HighIncomeNationUse
46 | + Sharing
47 | + Notice
48 | + Attribution
49 | + ShareAlike
50 | + SourceCode
51 | + Copyleft
52 | + LesserCopyleft
53 | + CommercialUse
54 | + attributionName
55 | + attributionURL
56 | - http://purl.org/dc/terms/
57 | + title
58 | + type
59 | - http://xmlns.com/foaf/0.1/#
60 |
61 | Currently the development process is focused on supporting more predcates from the list above, that would also lead to changing "-"/"±"s in the UI list to "+"s.
62 |
--------------------------------------------------------------------------------
/doc/WEBSITES:
--------------------------------------------------------------------------------
1 | Reference websites for testing purposes
2 | ---------------------------------------
3 |
4 | Currently the following sites are used:
5 |
6 | * http://en.wikipedia.org/
7 | * http://xkcd.com/
8 | * http://lessig.org/
9 | * http://www.flickr.com/photos/photomek/4508432287/
10 | * http://www.whitehouse.gov/copyright/
11 |
12 | This list may expand as more predicates get implemented from the FUNCSPEC.
13 |
--------------------------------------------------------------------------------
/doc/proposal.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/doc/proposal.pdf
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/icon.png
--------------------------------------------------------------------------------
/install.rdf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 | firefox@openattribute.com
9 |
10 |
13 | 0.9.8
14 |
15 |
20 | 2
21 |
22 |
23 | OpenAttribute
24 |
25 |
26 | Displays license and attribution information for Creative Commons licensed content.
27 |
28 |
29 | OpenAttribute Project
30 | Nathan Yergler (nathan@yergler.net)
31 | Pat Lockley
32 | Igor Lukanin (mail@igor.lukanin.name)
33 |
34 |
37 | http://openattribute.com
38 |
39 | chrome://ccffext/skin/icon32.png
40 | chrome://ccffext/skin/icon64.png
41 |
42 |
43 |
44 |
45 |
46 | {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
47 |
51 | 3.6
52 | 9.0a1
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/locale/en-US/locale.properties:
--------------------------------------------------------------------------------
1 | # Default localization (English, United States)
2 |
3 |
4 | # This localization file uses plural forms. See http://developer.mozilla.org/en/docs/Localization_and_Plurals
5 |
6 | # The number of the plural rule as defined at the link above
7 | l10n.plural.rule=1
8 |
9 |
10 | # A tab in the "Page Info" dialog
11 |
12 | # Label of the tab title
13 | tab.title.label=Licensing
14 |
15 | # Key to be used with to activate the tab using keyboard
16 | tab.title.key=L
17 |
18 | # Label showing the number of licensed objects
19 | label.number.label=This page contains one licensed object:;This page contains %d licensed objects:
20 |
21 | # Label of the checkbox used to highlight licensed objects
22 | checkbox.highlight.label=Highlight objects on this page
23 |
24 | # A string used to refer to a licensed object when its equal to the current document
25 | object.title.current-page.label=This page
26 |
27 | # The source URI of the licensed object
28 | object.source.title.label=Source:
29 |
30 | # The name of the license for the object
31 | object.license.title.label=License:
32 |
33 | # The permissions of the license for the object
34 | object.license.permissions.title.label=Permissions:
35 |
36 | # The Reproduction permission of the license for the object
37 | object.license.permissions.reproduction.label=reproduction
38 |
39 | # The Distribution permission of the license for the object
40 | object.license.permissions.distribution.label=distribution
41 |
42 | # The Derivative Works permission of the license for the object
43 | object.license.permissions.derivativeworks.label=derivative works
44 |
45 | # The High Income Nation Use permission of the license for the object
46 | object.license.permissions.highincomenationuse.label=high income nation use
47 |
48 | # The Sharing permission of the license for the object
49 | object.license.permissions.sharing.label=sharing
50 |
51 | # The requirements of the license for the object
52 | object.license.requirements.title.label=Requirements:
53 |
54 | # The Notice requirement of the license for the object
55 | object.license.requirements.notice.label=notice
56 |
57 | # The Attribution requirement of the license for the object
58 | object.license.requirements.attribution.label=attribution
59 |
60 | # The Share Alike requirement of the license for the object
61 | object.license.requirements.sharealike.label=share alike
62 |
63 | # The Source Code requirement of the license for the object
64 | object.license.requirements.sourcecode.label=source code
65 |
66 | # The Copyleft requirement of the license for the object
67 | object.license.requirements.copyleft.label=copyleft
68 |
69 | # The Lesser Copyleft requirement of the license for the object
70 | object.license.requirements.lessercopyleft.label=lesser copyleft
71 |
72 | # The prohibitions of the license for the object
73 | object.license.prohibitions.title.label=Prohibitions:
74 |
75 | # The Commercial Use prohibition of the license for the object
76 | object.license.prohibitions.commercialuse.label=commercial use
77 |
78 | object.attribution.label=Attribute This Work
79 |
80 | # The button for attribution information
81 | object.button.attribution.label=Attribution information
82 |
83 | # Key to be used with to activate the button for attribution information
84 | object.button.attribution.key=A
85 |
86 | copy=Copy
87 |
88 | # Key to be used with to copy attribution information to the clipboard as HTML
89 | object.button.attributionashtml.key=C
90 |
91 | # The button for copying attribution information to the clipboard as plain text
92 | object.button.attributionastext.label=Copy to clipboard as plain text
93 |
94 | # Key to be used with to copy attribution information to the clipboard as plain text
95 | object.button.attributionastext.key=T
96 |
97 | # The type of the object
98 | object.type.title.label=Type:
99 |
100 | # The Collection type of the object
101 | object.type.collection.label=collection
102 |
103 | # The Dataset type of the object
104 | object.type.dataset.label=dataset
105 |
106 | # The Event type of the object
107 | object.type.event.label=event
108 |
109 | # The Image type of the object
110 | object.type.image.label=image
111 |
112 | # The InteractiveResource type of the object
113 | object.type.interactiveresource.label=interactive resource
114 |
115 | # The MovingImage type of the object
116 | object.type.movingimage.label=moving image
117 |
118 | # The PhysicalObject type of the object
119 | object.type.physicalobject.label=physical object
120 |
121 | # The Service type of the object
122 | object.type.service.label=service
123 |
124 | # The Software type of the object
125 | object.type.software.label=software
126 |
127 | # The Sound type of the object
128 | object.type.sound.label=sound
129 |
130 | # The StillImage type of the object
131 | object.type.stillimage.label=stillimage
132 |
133 | # The Text type of the object
134 | object.type.text.label=text
135 |
136 | # The author of the object
137 | object.author.title.label=Author:
138 |
139 |
140 | # The location bar icon
141 |
142 | # The mouse-over label of the icon
143 | icon.title.label=This page contains one licensed object;This page contains %d licensed objects
--------------------------------------------------------------------------------
/locale/ru-RU/locale.properties:
--------------------------------------------------------------------------------
1 | # Russian localization (Russia)
2 |
3 |
4 | # This localization file uses plural forms. See http://developer.mozilla.org/en/docs/Localization_and_Plurals
5 |
6 | # The number of the plural rule as defined at the link above
7 | l10n.plural.rule=7
8 |
9 |
10 | # A tab in the "Page Info" dialog
11 |
12 | # Label of the tab title
13 | tab.title.label=Лицензии
14 |
15 | # Key to be used with to activate the tab using keyboard
16 | tab.title.key=Л
17 |
18 | # Label showing the number of licensed objects
19 | label.number.label=На странице один объект с лицензией:;На странице %d объекта с лицензиями;На странице %d объектов с лицензиями:
20 |
21 | # Label of the checkbox used to highlight licensed objects
22 | checkbox.highlight.label=Выделить объекты на странице
23 |
24 | # A string used to refer to a licensed object when its equal to the current document
25 | object.title.current-page.label=Эта страница
26 |
27 | # The source URI of the licensed object
28 | object.source.title.label=Источник:
29 |
30 | # The name of the license for the object
31 | object.license.title.label=Лицензия:
32 |
33 | # The permissions of the license for the object
34 | object.license.permissions.title.label=Разрешается:
35 |
36 | # The Reproduction permission of the license for the object
37 | object.license.permissions.reproduction.label=воспроизведение
38 |
39 | # The Distribution permission of the license for the object
40 | object.license.permissions.distribution.label=распространение
41 |
42 | # The Derivative Works permission of the license for the object
43 | object.license.permissions.derivativeworks.label=производные работы
44 |
45 | # The High Income Nation Use permission of the license for the object
46 | object.license.permissions.highincomenationuse.label=использование вне развивающихся стран
47 |
48 | # The Sharing permission of the license for the object
49 | object.license.permissions.sharing.label=платные производные работы при бесплатном распространении
50 |
51 | # The requirements of the license for the object
52 | object.license.requirements.title.label=Требуется:
53 |
54 | # The Notice requirement of the license for the object
55 | object.license.requirements.notice.label=сохранение информации о лицензии
56 |
57 | # The Attribution requirement of the license for the object
58 | object.license.requirements.attribution.label=указание авторства
59 |
60 | # The Share Alike requirement of the license for the object
61 | object.license.requirements.sharealike.label=сохранение лицензии для производных работ
62 |
63 | # The Source Code requirement of the license for the object
64 | object.license.requirements.sourcecode.label=предоставление исходного кода
65 |
66 | # The Copyleft requirement of the license for the object
67 | # TODO
68 | object.license.requirements.copyleft.label=copyleft
69 |
70 | # The Lesser Copyleft requirement of the license for the object
71 | # TODO
72 | object.license.requirements.lessercopyleft.label=lesser copyleft
73 |
74 | # The prohibitions of the license for the object
75 | object.license.prohibitions.title.label=Запрещается:
76 |
77 | # The Commercial Use prohibition of the license for the object
78 | object.license.prohibitions.commercialuse.label=коммерческое использование
79 |
80 | # The button for attribution information
81 | object.button.attribution.label=Информация об авторстве
82 |
83 | # Key to be used with to activate the button for attribution information
84 | object.button.attribution.key=А
85 |
86 | # The button for copying attribution information to the clipboard as HTML
87 | object.button.attributionashtml.label=Копировать в буфер обмена в формате HTML
88 |
89 | # Key to be used with to copy attribution information to the clipboard as HTML
90 | object.button.attributionashtml.key=H
91 |
92 | # The button for copying attribution information to the clipboard as plain text
93 | object.button.attributionastext.label=Копировать в буфер обмена в виде текста
94 |
95 | # Key to be used with to copy attribution information to the clipboard as plain text
96 | object.button.attributionastext.key=Т
97 |
98 | # The type of the object
99 | # TODO
100 | object.type.title.label=Type:
101 |
102 | # The Collection type of the object
103 | # TODO
104 | object.type.collection.label=collection
105 |
106 | # The Dataset type of the object
107 | # TODO
108 | object.type.dataset.label=dataset
109 |
110 | # The Event type of the object
111 | # TODO
112 | object.type.event.label=event
113 |
114 | # The Image type of the object
115 | # TODO
116 | object.type.image.label=image
117 |
118 | # The InteractiveResource type of the object
119 | # TODO
120 | object.type.interactiveresource.label=interactive resource
121 |
122 | # The MovingImage type of the object
123 | # TODO
124 | object.type.movingimage.label=moving image
125 |
126 | # The PhysicalObject type of the object
127 | # TODO
128 | object.type.physicalobject.label=physical object
129 |
130 | # The Service type of the object
131 | # TODO
132 | object.type.service.label=service
133 |
134 | # The Software type of the object
135 | # TODO
136 | object.type.software.label=software
137 |
138 | # The Sound type of the object
139 | # TODO
140 | object.type.sound.label=sound
141 |
142 | # The StillImage type of the object
143 | # TODO
144 | object.type.stillimage.label=stillimage
145 |
146 | # The Text type of the object
147 | # TODO
148 | object.type.text.label=text
149 |
150 | # The author of the object
151 | object.author.title.label=Автор:
152 |
153 |
154 | # The location bar icon
155 |
156 | # The mouse-over label of the icon
157 | icon.title.label=На странице один объект с лицензией;На странице %d объекта с лицензиями;На странице %d объектов с лицензиями
--------------------------------------------------------------------------------
/module/bootstrap.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Code modules inclusion.
3 | * Note that the modules are loaded only once
4 | *
5 | * @see https://developer.mozilla.org/en/Components.utils.import
6 | */
7 | {
8 | Components.utils.import("resource://ccffext/rdfa.js");
9 | Components.utils.import("resource://ccffext/ccffext.js");
10 | Components.utils.import("resource://ccffext/license.js");
11 | }
--------------------------------------------------------------------------------
/module/ccffext.js:
--------------------------------------------------------------------------------
1 | var EXPORTED_SYMBOLS = ["ccffext"];
2 |
3 | // Support for l10n of plurals
4 | Components.utils.import("resource://gre/modules/PluralForm.jsm");
5 |
6 | // RDFa parser
7 | Components.utils.import("resource://ccffext/rdfa.js");
8 |
9 | /**
10 | * Main extension object.
11 | * Behaves as a namespace for all code
12 | */
13 | var ccffext =
14 | {
15 | /**
16 | * An extension of the "Array" object's prototype.
17 | * Inspired by Shamasis Bhattacharya's code
18 | *
19 | * @return array An array of unique items
20 | * @see http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/
21 | */
22 | unique : function(in_array)
23 | {
24 | var object = {}, result = [];
25 |
26 | for(let i = 0; i < in_array.length; ++i)
27 | {
28 | object[in_array[i]] = in_array[i];
29 | }
30 |
31 | for(let i in object)
32 | {
33 | result.push(object[i]);
34 | }
35 |
36 | return result;
37 | },
38 |
39 | /**
40 | * Localization object that is used to fetch localized strings from a property file
41 | **/
42 | l10n :
43 | {
44 | /**
45 | * Bundle holding all strings
46 | **/
47 | bundle : Components.classes["@mozilla.org/intl/stringbundle;1"]
48 | .getService(Components.interfaces.nsIStringBundleService)
49 | .createBundle("chrome://ccffext/locale/locale.properties"),
50 |
51 | /**
52 | * A lazy-initialized function for getting plurals
53 | *
54 | * @param number The number to be shown
55 | * @param string A semicolon-separated string of plural forms containing the "%d" placeholders
56 | * @return string A plural form with the placeholder substituted with the number
57 | **/
58 | getPlural : undefined,
59 |
60 | /**
61 | * Fetched a string by its name from the bundle
62 | *
63 | * @param name The name of a string
64 | * @return string A string
65 | */
66 | get : function(name,number)
67 | {
68 | if ("undefined" == typeof number)
69 | {
70 | // No plural forms, just get the string
71 | return ccffext.l10n.bundle.GetStringFromName(name);
72 | }
73 | else
74 | {
75 | // Lazy-initialize the "getPlural" function
76 | if ("undefined" == typeof ccffext.l10n.getPlural)
77 | {
78 | ccffext.l10n.getPlural = PluralForm
79 | .makeGetter(ccffext.l10n.bundle.GetStringFromName("l10n.plural.rule"))[0];
80 | }
81 |
82 | // Find appropriate plural form, substitute the placeholder
83 | return ccffext.l10n.getPlural(
84 | number,
85 | ccffext.l10n.bundle.GetStringFromName(name))
86 | .replace("%d",number);
87 | }
88 | }
89 | },
90 |
91 | /**
92 | * Cache of analysed pages that is used to store the RDFa information.
93 | * The "hashing" approach is used
94 | *
95 | * @see http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/
96 | **/
97 | cache :
98 | {
99 | /**
100 | * The cache backend, initially empty
101 | **/
102 | values : {},
103 |
104 | /**
105 | * Checks if the cache contains an object by its key
106 | *
107 | * @param key A key
108 | * @return boolean True if the cache contains the object, false otherwise
109 | **/
110 | contains : function(key)
111 | {
112 | return undefined != ccffext.cache.values[key];
113 |
114 | },
115 |
116 |
117 | containsDocument : function(document)
118 | {
119 | var key = document.location.href;
120 | var lastModified = document.lastModified;
121 |
122 | return ((undefined != ccffext.cache.values[key]) &&
123 | (lastModified == ccffext.cache.values[key]["__lastModified"]));
124 |
125 | },
126 |
127 | /**
128 | * Stores a "key-object" pair in the cache
129 | *
130 | * @param key A key
131 | * @param object An object
132 | **/
133 | put : function(key,object)
134 | {
135 | ccffext.cache.values[key] = object;
136 | },
137 |
138 | putDocument : function(location,last_modified,object)
139 | {
140 | ccffext.cache.values[location] = object;
141 | ccffext.cache.values[location]["__lastModified"] = last_modified;
142 | },
143 |
144 | /**
145 | * Fetches an object by its key from the cache
146 | *
147 | * @param key A key
148 | **/
149 | get : function(key)
150 | {
151 | return ccffext.cache.values[key];
152 | }
153 | },
154 |
155 | /**
156 | * Licensed objects (RDFa subjects) methods
157 | **/
158 | objects :
159 | {
160 | /**
161 | * Top-level predicates that mark licensed objects
162 | **/
163 | predicates : ["http://www.w3.org/1999/xhtml/vocab#license",
164 | "http://creativecommons.org/ns#license",
165 | "http://purl.org/dc/terms/license"
166 | ],
167 |
168 | /**
169 | * Returns a list of subjects with license assertions from a document.
170 | *
171 | * @param doc_uri The URI of the document containing the license assertions.
172 | * @return array Array of objects
173 | */
174 | getLicensedSubjects : function(doc_uri)
175 | {
176 |
177 | // get the set of statements extracted from the location
178 | let statements = ccffext.cache.get(doc_uri).statements;
179 | // get an array of subjects which have a license predicate
180 | var subjects = [s.subject for each (s in statements)
181 | if (ccffext.objects.predicates.indexOf(s.predicate.uri) > -1)];
182 |
183 | return ccffext.unique(subjects);
184 | },
185 |
186 | /**
187 | * Returns an array of "predicate-object" pairs for the specified subject
188 | *
189 | * @param doc_uri The URI of the document containing the assertions.
190 | * @param subject The subject object to return predicate-object pairs for.
191 | * @return array An array of two element (predicate, object) arrays.
192 | */
193 | getPredObjPairs : function(doc_uri,subject)
194 | {
195 | var pairs = [];
196 |
197 | let statements = ccffext.cache.get(doc_uri).statements;
198 | for (let i = 0; i < statements.length; ++i)
199 | {
200 | if (statements[i].subject.uri == subject.uri)
201 | {
202 | pairs.push([statements[i].predicate,statements[i].object]);
203 | }
204 | }
205 |
206 | return pairs;
207 | },
208 |
209 | /**
210 | * Return the first object for the given subject and predicate.
211 | *
212 | * @param doc_uri The URI of the document containing the assertions.
213 | * @param subject The subject object to match.
214 | * @predicates array Predicates to search for, in order of preference.
215 | * @return array An array of two element (predicate, object) arrays.
216 | *
217 | **/
218 | getValue : function(doc_uri, subject, predicates) {
219 |
220 | for each (let p in predicates) {
221 | for (let i = 0,
222 | pairs = ccffext.objects.getPredObjPairs(doc_uri,subject);
223 | i < pairs.length; ++i) {
224 | if (pairs[i][0].uri == p) {
225 | return pairs[i][1];
226 | }
227 | }
228 | }
229 |
230 | return undefined;
231 |
232 | }, // getValue
233 |
234 | /**
235 | * Parses RDFa data of the given document and stores it in the cache
236 | *
237 | * @param location A string containing the URL of the document.
238 | * @param document The document to be parsed
239 | */
240 | parse : function(location, document)
241 | {
242 |
243 | XH.transform(document.getElementsByTagName("body")[0]);
244 | XH.transform(document.getElementsByTagName("head")[0]);
245 |
246 | RDFA.reset();
247 | RDFA.parse(document);
248 |
249 | ccffext.cache.putDocument(location, document.lastModified, RDFA.triplestore);
250 |
251 | // Apply any site-specific hacks
252 | // -- these hacks are applied for high value adopters, who
253 | // -- for whatever reason have adopted, ahem, imperfectly.
254 | Components.utils.import("resource://ccffext/hacks/hacks.js");
255 | for each (hack in ccffext_site_hacks.match(location)) {
256 | hack(ccffext.cache.get(location), location, document);
257 | }
258 |
259 | // see if the document contains license information
260 | ccffext.objects.callbackify(
261 | document,
262 | function (doc, objects) {
263 |
264 | Components.utils.import("resource://ccffext/license.js");
265 |
266 | // this document has licensed objects;
267 | // get the list of uncached licenses to retrieve
268 | // and pass each to the license loader
269 | [l.uri for each (l in ccffext.unique([
270 | ccffext.objects.getLicense(location, subject)
271 | for each (subject in objects)
272 | if ("undefined" !== typeof subject)]) )
273 | if ("undefined" !== typeof l &&
274 | !ccffext.cache.contains(l.uri))]
275 |
276 | .forEach(licenses.load);
277 |
278 | });
279 |
280 | },
281 |
282 | /**
283 | * Checks if the cache contains the information for a document, calling a callback if not. Then calls a callback
284 | * if the document has any licensed objects
285 | */
286 | callbackify : function(document,callbackHas,callbackNotCached)
287 | {
288 | const location = document.location.href;
289 |
290 | // For all pages, except for system ones like "about:blank", "about:config" and so on
291 | if (! location.match(/^about\:/i))
292 | {
293 | if (! ccffext.cache.containsDocument(document) &&
294 | "function" == typeof callbackNotCached)
295 | {
296 | callbackNotCached(document);
297 | }
298 |
299 | if (ccffext.cache.containsDocument(document))
300 | {
301 | const objects = ccffext.objects.getLicensedSubjects(location);
302 |
303 | if (0 < objects.length && "function" == typeof callbackHas)
304 | {
305 | callbackHas(document,objects);
306 | }
307 | }
308 | }
309 | },
310 |
311 | /**
312 | * Returns a title for a licensed subject.
313 | *
314 | * @param doc_uri URI of the document to search assertions from.
315 | * @param subject The subject to return the title of
316 | */
317 | getTitle : function(doc_uri, subject)
318 | {
319 | return ccffext.objects.getValue(
320 | doc_uri, subject,
321 | ["http://purl.org/dc/terms/title",
322 | "http://purl.org/dc/elements/1.1/title"]);
323 | },
324 |
325 | /**
326 | * Returns the display title for a licensed subject. If no
327 | * title is available, and the subject URI is the same as the
328 | * document URI, return a localized string for "this page".
329 | *
330 | * @param doc_uri URI of the document to search assertions from.
331 | * @param subject The subject to return the title of
332 | */
333 | getDisplayTitle : function(doc_uri, subject) {
334 |
335 | var title = ccffext.objects.getTitle(doc_uri, subject);
336 |
337 | if (typeof title != "undefined") return title;
338 |
339 | return doc_uri == subject.uri
340 | ? ccffext.l10n.get("object.title.current-page.label")
341 | : subject.uri;
342 | }, // getDisplayTitle
343 |
344 | /**
345 | * Returns a type for a licensed object
346 | *
347 | * @param doc_uri URI of the document to search assertions from.
348 | * @param object The object
349 | */
350 | getType : function(doc_uri,object)
351 | {
352 | var type = ccffext.objects.getValue(
353 | doc_uri, object,
354 | ["http://purl.org/dc/terms/type",
355 | "http://purl.org/dc/elements/1.1/type"]);
356 |
357 | if ("undefined" != typeof type)
358 | return type.uri.replace("http://purl.org/dc/dcmitype/","");
359 |
360 | return undefined;
361 | },
362 |
363 | /**
364 | * Returns an author for a licensed object
365 | *
366 | * @param doc_uri URI of the document to search assertions from.
367 | * @param object The object
368 | */
369 | getAuthor : function(doc_uri,object)
370 | {
371 | return ccffext.objects.getValue(
372 | doc_uri, object,
373 | ["http://creativecommons.org/ns#attributionName"]);
374 | },
375 |
376 | /**
377 | * Returns an URI for an author for a licensed object
378 | *
379 | * @param doc_uri URI of the document to search assertions from.
380 | * @param object The object
381 | */
382 | getAuthorUri : function(doc_uri,object)
383 | {
384 | return ccffext.objects.getValue(
385 | doc_uri, object,
386 | ["http://creativecommons.org/ns#attributionURL"]);
387 | },
388 |
389 | /**
390 | * Returns a source for a licensed object.
391 | *
392 | * @param doc_uri URI of the document to search assertions from.
393 | * @param object The object
394 | */
395 | getSource : function(doc_uri, object)
396 | {
397 | return object.uri;
398 | },
399 |
400 | /**
401 | * Returns a source for a licensed object.
402 | *
403 | * @param doc_uri URI of the document to search assertions from.
404 | * @param object The object
405 | */
406 | getAttributionHtml : function(doc_uri, object) {
407 |
408 | Components.utils.import("resource://ccffext/license.js");
409 |
410 | // get the license and other bits of information for this object
411 | license = licenses.getLicenseInfo(
412 | ccffext.objects.getLicense(doc_uri, object).uri);
413 |
414 | title = ccffext.objects.getTitle(doc_uri, object);
415 |
416 | identifier_name = null;
417 | identifier_url = null;
418 |
419 | attrib_name = ccffext.objects.getAuthor(doc_uri, object);
420 | attrib_url = ccffext.objects.getAuthorUri(doc_uri, object);
421 |
422 | // create the pieces for the attribution HTML
423 | attrib_pieces = new Array();
424 | attrib_ns = new Object();
425 |
426 | // -- title
427 | if (title) {
428 | attrib_pieces.push(
429 | '' + title + ''
430 | );
431 | attrib_ns["dct"] = "http://purl.org/dc/terms/";
432 | } else {
433 |
434 | // no attribution metadata available
435 | attrib_pieces.push(
436 | 'Work found at ' +
437 | object.uri + ' ');
438 | }
439 |
440 | // -- attrib name/URL
441 | if ("undefined" != typeof attrib_name ||
442 | "undefined" != typeof attrib_url) {
443 | // we have at least one of the name + URL
444 | if ("undefined" == typeof attrib_url) {
445 | // no attribution URL
446 | attrib_pieces.push(
447 | '' +
448 | attrib_name + ''
449 | );
450 |
451 | } else // we have the attribution URL; see if we have the name
452 | if ("undefined" == typeof attrib_name) {
453 |
454 | // w/o attrib_name we include the URL as the link text
455 | // but do not annotate it as the attribution name
456 | attrib_pieces.push(
457 | '' +
459 | attrib_url.uri +
460 | ''
461 | );
462 |
463 | } else {
464 |
465 | attrib_pieces.push(
466 | '' +
469 | attrib_name + ''
470 | );
471 | }
472 |
473 | attrib_ns["cc"] = "http://creativecommons.org/ns#";
474 | } // attribution name/url
475 |
476 | // -- identifier / publisher
477 | // -- XXX this is currently unimplemented, needed for PDM support
478 |
479 | // -- license
480 | attrib_pieces.push(
481 | '' +
482 | (license.identifier ? license.identifier : license.name) +
483 | '');
484 |
485 | // assemble the final HTML from the pieces
486 | attrib_html = '" + attrib_pieces.join(" / ") + "";
492 |
493 | return attrib_html;
494 |
495 | }, // getAtttributionHtml
496 |
497 | getAttributionText : function (doc_uri, object) {
498 |
499 | Components.utils.import("resource://ccffext/license.js");
500 |
501 | // get the license and other bits of information for this object
502 | license = licenses.getLicenseInfo(
503 | ccffext.objects.getLicense(doc_uri, object).uri);
504 |
505 | title = ccffext.objects.getTitle(doc_uri, object);
506 |
507 | identifier_name = null;
508 | identifier_url = null;
509 |
510 | attrib_name = ccffext.objects.getAuthor(doc_uri, object);
511 | attrib_url = ccffext.objects.getAuthorUri(doc_uri, object);
512 |
513 | // create the pieces for the attribution HTML
514 | attrib_pieces = new Array();
515 |
516 | // -- title
517 | if (title) {
518 | attrib_pieces.push( title + " (" + object.uri + ")" );
519 | } else {
520 |
521 | // no attribution metadata available
522 | attrib_pieces.push('Work found at ' + object.uri);
523 |
524 | }
525 |
526 | // -- attrib name/URL
527 | if ("undefined" != typeof attrib_name ||
528 | "undefined" != typeof attrib_url) {
529 | // we have at least one of the name + URL
530 | if ("undefined" == typeof attrib_url) {
531 | // no attribution URL
532 | attrib_pieces.push(attrib_name);
533 |
534 | } else // we have the attribution URL; see if we have the name
535 | if ("undefined" == typeof attrib_name) {
536 |
537 | // w/o attrib_name we include the URL as the link text
538 | // but do not annotate it as the attribution name
539 | attrib_pieces.push("(" + attrib_url.uri + ")");
540 |
541 | } else {
542 |
543 | attrib_pieces.push(
544 | attrib_name + " (" + attrib_url.uri + ")");
545 | }
546 |
547 | } // attribution name/url
548 |
549 | // -- identifier / publisher
550 | // -- XXX this is currently unimplemented, needed for PDM support
551 |
552 | // -- license
553 | attrib_pieces.push(
554 | (license.identifier ? license.identifier : license.name) +
555 | " (" + license.uri + ")");
556 |
557 | // assemble the final text from the pieces
558 | attrib_text = attrib_pieces.join(" / ");
559 |
560 | return attrib_text;
561 | }, // getAttributionText
562 |
563 | // Return the license for the specified object
564 | getLicense : function(doc_uri, object) {
565 |
566 | return ccffext.objects.getValue(
567 | doc_uri, object, ccffext.objects.predicates);
568 |
569 | },
570 | },
571 |
572 | /**
573 | * Utility function that writes a message to the JavaScript console
574 | *
575 | * @param message A message
576 | */
577 | log : function(message)
578 | {
579 | Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService)
580 | .logStringMessage(message);
581 | }
582 | };
--------------------------------------------------------------------------------
/module/hacks/hacks.js:
--------------------------------------------------------------------------------
1 | var EXPORTED_SYMBOLS = ["ccffext_site_hacks"];
2 |
3 | var ccffext_site_hacks = new function Hacks() {
4 |
5 | var that = this;
6 | var _hacks = new Array();
7 |
8 | this.register = function (name, regex, callback) {
9 |
10 | _hacks.push([name, regex, callback]);
11 |
12 | };
13 |
14 | this.match = function (location) {
15 | return [h[2] for each (h in _hacks)
16 | if (h[1].test(location))];
17 | };
18 |
19 | /**
20 | *
21 | * Evaluate a series of xpath expressions against a document,
22 | * returning the string value of the first that has a match.
23 | *
24 | * exprs is an Array of xpath expressions
25 | *
26 | **/
27 | this.evaluateXpath = function (document, exprs) {
28 |
29 | for each (ex in exprs) {
30 | var result = document.evaluate(ex,
31 | document,
32 | null,
33 | Components.interfaces.nsIDOMXPathResult.STRING_TYPE,
34 | null).stringValue;
35 | if (("undefined" != typeof result) && (result.trim() != '')) {
36 | return result;
37 | }
38 | }
39 |
40 | return undefined;
41 |
42 | };
43 |
44 | };
45 |
46 | // register site hacks here
47 |
48 | // Flickr
49 | ccffext_site_hacks.register(
50 | "flickr", /^https?\:\/\/(www\.)?flickr\.com\/.*/,
51 | function(triples, location, document) {
52 |
53 | Components.utils.import("resource://ccffext/rdfa.js");
54 |
55 | var author = ccffext_site_hacks.evaluateXpath(
56 | document,
57 | ["//span[@class='realname']/span/a",
58 | "//strong[@class='username']/a"]);
59 |
60 | if ("undefined" != typeof author) {
61 | triples.add(new RDFSymbol(location),
62 | new RDFSymbol("http://creativecommons.org/ns#attributionName"),
63 | new RDFLiteral(author.trim()), 'RDFa');
64 | }
65 |
66 | var authorUri = ccffext_site_hacks.evaluateXpath(
67 | document,
68 | ["//strong[@class='username']/a/@href"]);
69 |
70 | if ("undefined" != typeof authorUri) {
71 | triples.add(new RDFSymbol(location),
72 | new RDFSymbol("http://creativecommons.org/ns#attributionURL"),
73 | new RDFSymbol(Util.uri.join(authorUri, location)),
74 | 'RDFa');
75 |
76 | }
77 | }); // flickr
78 |
79 | // Wikipedia(s)
80 | ccffext_site_hacks.register(
81 | "wikipedia", /^(http\:\/\/[a-z]+\.wikipedia\.org\/wiki\/.*)|(https\:\/\/secure\.wikimedia\.org\/wikipedia\/[a-z]+\/wiki\/.*)/,
82 | function(triples, location, document) {
83 |
84 | // import the RDFa library
85 | Components.utils.import("resource://ccffext/rdfa.js");
86 |
87 | // Wikipedias are licensed CC BY-SA 3.0 Unported
88 | // remove any license triples that may have crept in
89 | // (enwp uses rel="license", others do not)
90 | triples.removeMany(new RDFSymbol(location),
91 | new RDFSymbol("http://www.w3.org/1999/xhtml/vocab#license"),
92 | undefined,
93 | 'RDFa');
94 |
95 | // add the license triple
96 | triples.add(new RDFSymbol(location),
97 | new RDFSymbol("http://www.w3.org/1999/xhtml/vocab#license"),
98 | new RDFSymbol("http://creativecommons.org/licenses/by-sa/3.0/"),
99 | 'RDFa');
100 | }); // wikipedia
--------------------------------------------------------------------------------
/module/license.js:
--------------------------------------------------------------------------------
1 | var EXPORTED_SYMBOLS = ["licenses"];
2 |
3 | Components.utils.import("resource://ccffext/ccffext.js");
4 |
5 | var licenses = new function Licenses() {
6 |
7 | var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
8 | var license_frame = null, queue = new Array(), working = false, _current_callback = null, _current_license;
9 | var that = this;
10 |
11 | this.init = function (browser) {
12 |
13 | browser.webNavigation.allowAuth = true;
14 | browser.webNavigation.allowImages = false;
15 | browser.webNavigation.allowJavascript = false;
16 | browser.webNavigation.allowMetaRedirects = true;
17 | browser.webNavigation.allowPlugins = false;
18 | browser.webNavigation.allowSubframes = false;
19 |
20 | // attach the event listener which will handle parsing licenses
21 | browser.addEventListener(
22 | "DOMContentLoaded", onDomLoaded, true);
23 |
24 | // store a reference to the browser
25 | license_frame = browser;
26 |
27 | // reset the internal state flag
28 | working = false;
29 |
30 | }; // init
31 |
32 | this.load = function (license_uri, callback) {
33 | // refuse to queue something we won't be able to handle
34 | if ("string" == typeof license_uri) {
35 | queue.push([that.normalizeLicenseUri(license_uri), callback]);
36 | }
37 |
38 | // and check the queue...
39 | check_queue();
40 |
41 | }; // load
42 |
43 | this.notify = function (timer) {
44 | check_queue();
45 | };
46 |
47 | this.normalizeLicenseUri = function (license_uri) {
48 |
49 | if ("string" == typeof license_uri &&
50 | license_uri.indexOf("http://creativecommons.org/") == 0) {
51 | // This is a Creative Commons license;
52 | // make sure we're using the canonical URI
53 | if (license_uri.lastIndexOf("/") < license_uri.length - 1) {
54 | // strip off the trailing bit
55 | license_uri = license_uri.slice(0, license_uri.lastIndexOf("/") + 1);
56 | }
57 | }
58 |
59 | return license_uri;
60 |
61 | }; // normalizeLicenseUri
62 |
63 | /**
64 | * Returns information about the license
65 | *
66 | * @param license_uri The URI of the license to load
67 | * @param callback Callback when the license details have been retrieved;
68 | * @param cb_args An array to be passed into the callback
69 | *
70 | * This is called with the signature (license, cb_args).
71 | **/
72 | this.getLicenseInfo = function(license_uri, callback, cb_args) {
73 |
74 | var license = {
75 | name : undefined,
76 | uri : undefined,
77 | identifier : undefined,
78 | code: undefined,
79 | color : undefined
80 | };
81 |
82 | license.uri = license.name = that.normalizeLicenseUri(license_uri);
83 |
84 | if (ccffext.cache.contains(license.uri)) {
85 | // this license has already been loaded
86 | // retrieve the details from the RDF store
87 | populateLicenseObject(license);
88 |
89 | // call the callback
90 | if ("undefined" != typeof callback) {
91 | callback (license, cb_args);
92 | }
93 | } else
94 |
95 | // retrieve the license document to introspect for RDFa
96 | if ("undefined" != typeof license_frame) {
97 |
98 | // the have a browser reference, retrieve the license
99 | that.load(license.uri,
100 | function(url) {
101 | populateLicenseObject(license);
102 |
103 | // call the callback when done
104 | callback (license, cb_args);
105 | });
106 |
107 | } // if a license browser is available
108 | else
109 |
110 | // make sure the call back happens,
111 | // even if we can't load the license
112 | if ("undefined" != typeof callback) {
113 | callback (license, cb_args);
114 | }
115 |
116 | return license;
117 |
118 | }; // getLicenseInfo
119 |
120 | function populateLicenseObject(license) {
121 |
122 | license.name = ccffext.objects.getValue(
123 | license.uri, {'uri':license.uri},
124 | ["http://purl.org/dc/terms/title",
125 | "http://purl.org/dc/elements/1.1/title"]);
126 |
127 | if ("object" == typeof license.name)
128 | license.name = license.name.toString();
129 |
130 | if ("string" == typeof license.name)
131 | license.name = license.name.trim();
132 |
133 | license.identifier = ccffext.objects.getValue(
134 | license.uri, {'uri':license.uri},
135 | ["http://purl.org/dc/terms/identifier",
136 | "http://purl.org/dc/elements/1.1/identifier"]);
137 |
138 | if ("object" == typeof license.identifier)
139 | license.identifier = license.identifier.toString();
140 |
141 | if ("string" == typeof license.identifier)
142 | license.identifier = license.identifier.trim();
143 |
144 | if (license.uri.indexOf("http://creativecommons.org/") == 0) {
145 |
146 | var re_license_code = /http:\/\/creativecommons\.org\/(licenses|publicdomain)\/([a-z\-\+]+)\/.*/;
147 | license.code = license.uri.match(re_license_code)[2];
148 | } else {
149 | license.code = license.identifier;
150 | }
151 |
152 | // determine the license "color"
153 | // this is currently CC-specific
154 | switch (license.code) {
155 | case "by":
156 | case "by-sa":
157 | case "mark":
158 | case "zero":
159 | case "publicdomain":
160 | license.color = "green";
161 | break;
162 |
163 | case "by-nc":
164 | case "by-nd":
165 | case "by-nc-nd":
166 | case "by-nc-sa":
167 | case "sampling+":
168 | case "nc-sampling+":
169 | license.color = "yellow";
170 | break;
171 |
172 | case "sampling":
173 | case "devnations":
174 | license.color = "red";
175 | break;
176 | }; // switch on license code
177 |
178 | }; // populateLicenseObject
179 |
180 | function onDomLoaded (e) {
181 |
182 | var doc = e.originalTarget;
183 | var url = doc.location.href;
184 |
185 | // parse the license document for RDFa
186 | ccffext.objects.parse(_current_license, doc);
187 |
188 | // reset flags
189 | working = false;
190 |
191 | // see if there's anything else to process once we're done
192 | timer.initWithCallback(
193 | that,
194 | 100, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
195 |
196 | // call the callback when done
197 | if ("function" == typeof _current_callback)
198 | _current_callback (url);
199 |
200 | }; // onDomLoaded
201 |
202 | var check_queue = function () {
203 |
204 | if (queue.length > 0) {
205 | // see if we're currently working
206 | if (working == true) {
207 | timer.initWithCallback(
208 | that,
209 | 100, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
210 | return;
211 | }
212 |
213 | // make sure we're initialized
214 | if (license_frame === null) {
215 | timer.initWithCallback(
216 | that,
217 | 250, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
218 | return;
219 | };
220 |
221 | working = true;
222 |
223 | [_current_license, _current_callback] = queue.pop();
224 | license_frame.webNavigation.loadURI(
225 | _current_license,
226 | Components.interfaces.nsIWebNavigation, null, null, null);
227 | }
228 |
229 | }; // check_queue
230 |
231 | this.log = function(message)
232 | {
233 | Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService)
234 | .logStringMessage(message);
235 | };
236 | };
237 |
238 |
--------------------------------------------------------------------------------
/module/rdfa.js:
--------------------------------------------------------------------------------
1 | var EXPORTED_SYMBOLS = ["RDFA","XH", "RDFSymbol", "RDFLiteral", "Util"];
2 |
3 | /**
4 | * RDFA in Javascript
5 | * Ben Adida - ben@adida.net
6 | * Nathan Yergler - nathan@creativecommons.org
7 | * Jeni Tennison - jeni@jenitennison.com
8 | *
9 | * W3C Open Source License
10 | *
11 | * includes the AJAR (Tabulator) stuff
12 | */
13 | //
14 | //
15 | //
16 |
17 | // Implementing URI-specific functions
18 | //
19 | // See RFC 2386
20 | //
21 | // This is or was http://www.w3.org/2005/10/ajaw/uri.js
22 | // 2005 W3C open source licence
23 | //
24 | //
25 | // Take a URI given in relative or absolute form and a base
26 | // URI, and return an absolute URI
27 | //
28 | // See also http://www.w3.org/2000/10/swap/uripath.py
29 | //
30 |
31 | if (typeof Util == "undefined") { var Util = {}}
32 | if (typeof Util.uri == "undefined") { Util.uri = {}}
33 |
34 | Util.uri.join = function (given, base) {
35 | // if (typeof tabulator.log.debug != 'undefined') tabulator.log.debug(" URI given="+given+" base="+base)
36 | var baseHash = base.indexOf('#');
37 | if (baseHash > 0) base = base.slice(0, baseHash);
38 | if (given.length==0) return base // before chopping its filename off
39 | if (given.indexOf('#')==0) return base + given;
40 | var colon = given.indexOf(':');
41 | if (colon >= 0) return given // Absolute URI form overrides base URI
42 | var baseColon = base.indexOf(':');
43 | if (base == "") return given;
44 | if (baseColon < 0) {
45 | alert("Invalid base: "+ base + ' in join with ' +given);
46 | return given;
47 | }
48 | var baseScheme = base.slice(0,baseColon+1) // eg http:
49 | if (given.indexOf("//") == 0) // Starts with //
50 | return baseScheme + given;
51 | if (base.indexOf('//', baseColon)==baseColon+1) { // Any hostpart?
52 | var baseSingle = base.indexOf("/", baseColon+3)
53 | if (baseSingle < 0) {
54 | if (base.length-baseColon-3 > 0) {
55 | return base + "/" + given
56 | } else {
57 | return baseScheme + given
58 | }
59 | }
60 | } else {
61 | var baseSingle = base.indexOf("/", baseColon+1)
62 | if (baseSingle < 0) {
63 | if (base.length-baseColon-1 > 0) {
64 | return base + "/" + given
65 | } else {
66 | return baseScheme + given
67 | }
68 | }
69 | }
70 |
71 | if (given.indexOf('/') == 0) // starts with / but not //
72 | return base.slice(0, baseSingle) + given
73 |
74 | var path = base.slice(baseSingle)
75 | var lastSlash = path.lastIndexOf("/")
76 | if (lastSlash <0) return baseScheme + given
77 | if ((lastSlash >=0) && (lastSlash < (path.length-1)))
78 | path = path.slice(0, lastSlash+1) // Chop trailing filename from base
79 |
80 | path = path + given
81 | while (path.match(/[^\/]*\/\.\.\//)) // must apply to result of prev
82 | path = path.replace( /[^\/]*\/\.\.\//, '') // ECMAscript spec 7.8.5
83 | path = path.replace( /\.\//g, '') // spec vague on escaping
84 | return base.slice(0, baseSingle) + path
85 | }
86 |
87 | // refTo: Make a URI relative to a given base
88 | //
89 | // based on code in http://www.w3.org/2000/10/swap/uripath.py
90 | //
91 | Util.uri.commonHost = new RegExp("^[-_a-zA-Z0-9.]+:(//[^/]*)?/[^/]*$");
92 | Util.uri.refTo = function(base, uri) {
93 | if (!base) return uri;
94 | if (base == uri) return "";
95 | var i =0; // How much are they identical?
96 | while (i0 && uri[i-1] != '/') i--;
110 |
111 | if (i<3) return uri; // No way
112 | if ((base.indexOf('//', i-2) > 0) || uri.indexOf('//', i-2) > 0)
113 | return uri; // an unshared '//'
114 | if (base.indexOf(':', i) >0) return uri; // unshared ':'
115 | var n = 0;
116 | for (var j=i; j")
181 | }
182 |
183 | function toNT() {
184 | return RDFSymbol_toNT(this)
185 | }
186 |
187 | function RDFSymbol(uri) {
188 | this.uri = uri
189 | return this
190 | }
191 |
192 | RDFSymbol.prototype.termType = 'symbol'
193 | RDFSymbol.prototype.toString = toNT
194 | RDFSymbol.prototype.toNT = toNT
195 |
196 | // Some precalculaued symbols
197 |
198 | RDFSymbol.prototype.XSDboolean = new RDFSymbol('http://www.w3.org/2001/XMLSchema#boolean');
199 | RDFSymbol.prototype.integer = new RDFSymbol('http://www.w3.org/2001/XMLSchema#integer');
200 |
201 |
202 | // Blank Node
203 |
204 | var RDFNextId = 0; // Gobal genid
205 | var RDFGenidPrefix = "genid:"
206 | var NTAnonymousNodePrefix = "_:n"
207 |
208 | function RDFBlankNode(id) {
209 | // 2007-11-26 JT
210 | // uncommented out next three lines
211 | if (id != null) {
212 | this.id = id;
213 | } else {
214 | this.id = RDFNextId++
215 | }
216 | return this
217 | }
218 |
219 | RDFBlankNode.prototype.termType = 'bnode'
220 |
221 | RDFBlankNode.prototype.toNT = function() {
222 | return NTAnonymousNodePrefix + this.id
223 | }
224 | RDFBlankNode.prototype.toString = RDFBlankNode.prototype.toNT
225 |
226 | // Literal
227 |
228 | function RDFLiteral(value, lang, datatype) {
229 | this.value = value
230 | this.lang=lang; // string
231 | this.datatype=datatype; // term
232 | this.toString = RDFLiteralToString
233 | this.toNT = RDFLiteral_toNT
234 | return this
235 | }
236 |
237 | RDFLiteral.prototype.termType = 'literal'
238 |
239 | function RDFLiteral_toNT() {
240 | var str = this.value
241 | if (typeof str != 'string') {
242 | throw Error("Value of RDF literal is not string: "+str)
243 | }
244 | str = str.replace(/\\/g, '\\\\'); // escape
245 | str = str.replace(/\"/g, '\\"');
246 | str = '"' + str + '"' //'
247 |
248 | if (this.datatype){
249 | str = str + '^^' + this.datatype//.toNT()
250 | }
251 | if (this.lang) {
252 | str = str + "@" + this.lang
253 | }
254 | return str
255 | }
256 |
257 | function RDFLiteralToString() {
258 | return this.value
259 | }
260 |
261 | RDFLiteral.prototype.toString = RDFLiteralToString
262 | RDFLiteral.prototype.toNT = RDFLiteral_toNT
263 |
264 | function RDFCollection() {
265 | this.id = RDFNextId++
266 | this.elements = []
267 | this.closed = false
268 | }
269 |
270 | RDFCollection.prototype.termType = 'collection'
271 |
272 | RDFCollection.prototype.toNT = function() {
273 | return NTAnonymousNodePrefix + this.id
274 | }
275 | RDFCollection.prototype.toString = RDFCollection.prototype.toNT
276 |
277 | RDFCollection.prototype.append = function (el) {
278 | this.elements.push(el)
279 | }
280 | RDFCollection.prototype.unshift=function(el){
281 | this.elements.unshift(el);
282 | }
283 | RDFCollection.prototype.shift=function(){
284 | return this.elements.shift();
285 | }
286 |
287 | RDFCollection.prototype.close = function () {
288 | this.closed = true
289 | }
290 |
291 | // Statement
292 | //
293 | // This is a triple with an optional reason.
294 | //
295 | // The reason can point to provenece or inference
296 | //
297 | function RDFStatement_toNT() {
298 | return (this.subject.toNT() + " "
299 | + this.predicate.toNT() + " "
300 | + this.object.toNT() +" .")
301 | }
302 |
303 | function RDFStatement(subject, predicate, object, why) {
304 | this.subject = makeTerm(subject)
305 | this.predicate = makeTerm(predicate)
306 | this.object = makeTerm(object)
307 | if (typeof why !='undefined') {
308 | this.why = why
309 | } else if (RDFTracking) {
310 | //tabulator.log.debug("WARNING: No reason on "+subject+" "+predicate+" "+object)
311 | }
312 | return this
313 | }
314 |
315 | RDFStatement.prototype.toNT = RDFStatement_toNT
316 | RDFStatement.prototype.toString = RDFStatement_toNT
317 |
318 |
319 | // Formula
320 | //
321 | // Set of statements.
322 |
323 | function RDFFormula() {
324 | this.statements = []
325 | this.constraints = []
326 | this.initBindings = []
327 | this.optional = []
328 | this.superFormula = null;
329 | return this
330 | }
331 |
332 | function RDFFormula_toNT() {
333 | return "{\n" + this.statements.join('\n') + "}"
334 | }
335 |
336 | //RDFQueryFormula.prototype = new RDFFormula()
337 | //RDFQueryFormula.termType = 'queryFormula'
338 | RDFFormula.prototype.termType = 'formula'
339 | RDFFormula.prototype.toNT = RDFFormula_toNT
340 | RDFFormula.prototype.toString = RDFFormula_toNT
341 |
342 | RDFFormula.prototype.add = function(subj, pred, obj, why) {
343 | this.statements.push(new RDFStatement(subj, pred, obj, why))
344 | }
345 |
346 | // Convenience methods on a formula allow the creation of new RDF terms:
347 |
348 | RDFFormula.prototype.sym = function(uri,name) {
349 | if (name != null) {
350 | throw 'second parameter temporarily not supported';
351 | /*
352 | if (!tabulator.ns[uri]) throw 'The prefix "'+uri+'" is not set in the API';
353 | uri = tabulator.ns[uri] + name
354 | */
355 | }
356 | return new RDFSymbol(uri)
357 | }
358 |
359 | RDFFormula.prototype.literal = function(val, lang, dt) {
360 | return new RDFLiteral(val.toString(), lang, dt)
361 | }
362 |
363 | RDFFormula.prototype.bnode = function(id) {
364 | return new RDFBlankNode(id)
365 | }
366 |
367 | RDFFormula.prototype.formula = function() {
368 | return new RDFFormula()
369 | }
370 |
371 | RDFFormula.prototype.collection = function () { // obsolete
372 | return new RDFCollection()
373 | }
374 |
375 | RDFFormula.prototype.list = function (values) {
376 | li = new RDFCollection();
377 | if (values) {
378 | for(var i = 0; i other.classOrder) return +1
523 | if (this.value < other.value) return -1
524 | if (this.value > other.value) return +1
525 | return 0
526 | }
527 |
528 | RDFSymbol.prototype.compareTerm = function(other) {
529 | if (this.classOrder < other.classOrder) return -1
530 | if (this.classOrder > other.classOrder) return +1
531 | if (this.uri < other.uri) return -1
532 | if (this.uri > other.uri) return +1
533 | return 0
534 | }
535 |
536 | RDFBlankNode.prototype.compareTerm = function(other) {
537 | if (this.classOrder < other.classOrder) return -1
538 | if (this.classOrder > other.classOrder) return +1
539 | if (this.id < other.id) return -1
540 | if (this.id > other.id) return +1
541 | return 0
542 | }
543 |
544 | RDFCollection.prototype.compareTerm = RDFBlankNode.prototype.compareTerm
545 |
546 | // Convenience routines
547 |
548 | // Only one of s p o can be undefined, and w is optional.
549 | RDFFormula.prototype.each = function(s,p,o,w) {
550 | var results = []
551 | var st, sts = this.statementsMatching(s,p,o,w)
552 | var i, n=sts.length
553 | if (typeof s == 'undefined') {
554 | for (i=0; i1
610 | //
611 | // 2005-10 Written Tim Berners-Lee
612 | //
613 | //
614 |
615 | /*jsl:option explicit*/ // Turn on JavaScriptLint variable declaration checking
616 |
617 | var owl_ns = "http://www.w3.org/2002/07/owl#";
618 | var link_ns = "http://www.w3.org/2006/link#";
619 |
620 | /* hashString functions are used as array indeces. This is done to avoid
621 | ** conflict with existing properties of arrays such as length and map.
622 | ** See issue 139.
623 | */
624 | RDFLiteral.prototype.hashString = RDFLiteral.prototype.toNT;
625 | RDFSymbol.prototype.hashString = RDFSymbol.prototype.toNT;
626 | RDFBlankNode.prototype.hashString = RDFBlankNode.prototype.toNT;
627 | RDFCollection.prototype.hashString = RDFCollection.prototype.toNT;
628 |
629 | RDFIndexedFormula.prototype = new RDFFormula();
630 | RDFIndexedFormula.prototype.constructor = RDFIndexedFormula;
631 | // RDFIndexedFormula.superclass = RDFFormula.prototype;
632 | RDFIndexedFormula.SuperClass = RDFFormula;
633 |
634 | var RDFArrayRemove = function(a, x) { //removes all elements equal to x from a
635 | for(var i=0; i to f(F,s,p,o)
649 | this.classAction = []; // What to do when adding { s type X }
650 | this.redirection = []; // redirect to lexically smaller equivalent symbol
651 | this.aliases = []; // reverse mapping to redirection: aliases for this
652 | this.HTTPRedirects = []; // redirections we got from HTTP
653 | this.subjectIndex = []; // Array of statements with this X as subject
654 | this.predicateIndex = []; // Array of statements with this X as subject
655 | this.objectIndex = []; // Array of statements with this X as object
656 | this.namespaces = {} // Dictionary of namespace prefixes
657 | if (typeof features == 'undefined') features = ["sameAs",
658 | "InverseFunctionalProperty", "FunctionalProperty"];
659 | // this.features = features
660 |
661 | // Callbackify?
662 |
663 | function handleRDFType(formula, subj, pred, obj, why) {
664 | if (typeof formula.typeCallback != 'undefined')
665 | formula.typeCallback(formula, obj, why);
666 |
667 | var x = formula.classAction[obj.hashString()];
668 | if (x) return x(formula, subj, pred, obj);
669 | return false; // statement given is needed
670 | } //handleRDFType
671 |
672 | //If the predicate is #type, use handleRDFType to create a typeCallback on the object
673 | this.propertyAction[
674 | ''] = handleRDFType;
675 |
676 | // Assumption: these terms are not redirected @@fixme
677 | if (features.indexOf("sameAs") >=0)
678 | this.propertyAction[''] =
679 | function(formula, subj, pred, obj, why) {
680 | formula.equate(subj,obj);
681 | return true; // true if statement given is NOT needed in the store
682 | }; //sameAs -> equate & don't add to index
683 |
684 | function newPropertyAction(formula, pred, action) {
685 | formula.propertyAction[pred] = action;
686 | var toBeFixed = formula.statementsMatching(undefined, pred, undefined);
687 | var i;
688 | for (i=0; i= 0)
697 | this.classAction["<"+owl_ns+"InverseFunctionalProperty>"] =
698 | function(formula, subj, pred, obj, addFn) {
699 | return newPropertyAction(formula, subj, handle_IFP); // yes subj not pred!
700 | }; //IFP -> handle_IFP, do add to index
701 |
702 | if (features.indexOf("FunctionalProperty") >= 0)
703 | this.classAction["<"+owl_ns+"FunctionalProperty>"] =
704 | function(formula, subj, proj, obj, addFn) {
705 | return newPropertyAction(formula, subj, handle_FP);
706 | }; //FP => handleFP, do add to index
707 |
708 | function handle_IFP(formula, subj, pred, obj) {
709 | var s1 = formula.any(undefined, pred, obj);
710 | if (typeof s1 == 'undefined') return false; // First time with this value
711 | formula.equate(s1, subj);
712 | return true;
713 | } //handle_IFP
714 |
715 | function handle_FP(formula, subj, pred, obj) {
716 | var o1 = formula.any(subj, pred, undefined);
717 | if (typeof o1 == 'undefined') return false; // First time with this value
718 | formula.equate(o1, obj);
719 | return true ;
720 | } //handle_FP
721 |
722 | } /* end RDFIndexedFormula */
723 |
724 | var RDFPlainFormula = function() { return RDFIndexedFormula([]); } // No features
725 |
726 |
727 | RDFIndexedFormula.prototype.setPrefixForURI = function(prefix, nsuri) {
728 | //TODO:This is a hack for our own issues, which ought to be fixed post-release
729 | //See http://dig.csail.mit.edu/cgi-bin/roundup.cgi/tabulator/issue227
730 | if(prefix=="tab" && this.namespaces["tab"]) {
731 | return;
732 | }
733 | this.namespaces[prefix] = nsuri
734 | }
735 |
736 | // Deprocated ... name too generic
737 | RDFIndexedFormula.prototype.register = function(prefix, nsuri) {
738 | this.namespaces[prefix] = nsuri
739 | }
740 |
741 |
742 | /** simplify graph in store when we realize two identifiers are equal
743 |
744 | We replace the bigger with the smaller.
745 |
746 | */
747 | RDFIndexedFormula.prototype.equate = function(u1, u2) {
748 | //tabulator.log.info("Equating "+u1+" and "+u2)
749 |
750 | var d = u1.compareTerm(u2);
751 | if (!d) return true; // No information in {a = a}
752 | var big, small;
753 | if (d < 0) { // u1 less than u2
754 | return this.replaceWith(u2, u1);
755 | } else {
756 | return this.replaceWith(u1, u2);
757 | }
758 | }
759 |
760 | // Replace big with small, obsoleted with obsoleting.
761 | //
762 | RDFIndexedFormula.prototype.replaceWith = function(big, small) {
763 |
764 | var i, toBeFixed, hash;
765 | toBeFixed = this.statementsMatching(big, undefined, undefined);
766 | //tabulator.log.debug("Replacing "+big+" with "+small) // @@
767 |
768 | for (i=0; i < toBeFixed.length; i++) {
769 | toBeFixed[i].subject = small;
770 | hash = small.hashString();
771 | if (typeof this.subjectIndex[hash] == 'undefined')
772 | this.subjectIndex[hash] = [];
773 | this.subjectIndex[hash].push(toBeFixed[i]);
774 | }
775 | delete this.subjectIndex[big.hashString()];
776 |
777 | // If we allow equating predicates we must index them.
778 | toBeFixed = this.statementsMatching(undefined, big, undefined);
779 |
780 | for (i = 0; i < toBeFixed.length; i++) {
781 | toBeFixed[i].predicate = small;
782 | hash = small.hashString();
783 | if (typeof this.predicateIndex[hash] == 'undefined')
784 | this.predicateIndex[hash] = [];
785 | this.predicateIndex[hash].push(toBeFixed[i]);
786 | }
787 | delete this.predicateIndex[big.hashString()];
788 |
789 | toBeFixed = this.statementsMatching(undefined, undefined, big);
790 | for (i=0; i < toBeFixed.length; i++) {
791 | // RDFArrayRemove(this.objectIndex[big], st)
792 | toBeFixed[i].object = small;
793 | hash = small.hashString()
794 | if (typeof this.objectIndex[hash] == 'undefined')
795 | this.objectIndex[hash] = [];
796 | this.objectIndex[hash].push(toBeFixed[i]);
797 | }
798 | delete this.objectIndex[big.hashString()];
799 |
800 | this.redirection[big.hashString()] = small;
801 | if (big.uri) {
802 | if (typeof (this.aliases[small.hashString()]) == 'undefined')
803 | this.aliases[small.hashString()] = [];
804 | this.aliases[small.hashString()].push(big); // Back link
805 |
806 | this.add(small, this.sym('http://www.w3.org/2006/link#uri'), big.uri)
807 |
808 | // If two things are equal, and one is requested, we should request the other.
809 | if (this.sf) {
810 | this.sf.nowKnownAs(big, small)
811 | }
812 |
813 | }
814 |
815 | /* merge actions @@ assumes never > 1 action*/
816 | var action = this.classAction[big.hashString()];
817 | if ((typeof action != 'undefined') &&
818 | (typeof this.classAction[small.hashString()] == 'undefined')) {
819 | this.classAction[small.hashString()] = action;
820 | }
821 |
822 | action = this.propertyAction[big.hashString()];
823 | if ((typeof action != 'undefined') &&
824 | (typeof this.propertyAction[small.hashString()] == 'undefined')) {
825 | this.propertyAction[small.hashString()] = action;
826 | }
827 | // tabulator.log.debug("Equate done. "+big+" to be known as "+small)
828 | return true; // true means the statement does not need to be put in
829 | };
830 |
831 | // Return the symbol with canonical URI as smushed
832 | RDFIndexedFormula.prototype.canon = function(term) {
833 | var y = this.redirection[term.hashString()];
834 | if (typeof y == 'undefined') return term;
835 | return y;
836 | }
837 |
838 | // A list of all the URIs by which this thing is known
839 | RDFIndexedFormula.prototype.uris = function(term) {
840 | var cterm = this.canon(term)
841 | var terms = this.aliases[cterm.hashString()];
842 | if (!cterm.uri) return []
843 | var res = [ cterm.uri ]
844 | if (typeof terms != 'undefined') {
845 | for (var i=0; i';
936 | return (!!this.subjectIndex[hash] || !!this.objectIndex[hash]
937 | || !!this.predicateIndex[hash]);
938 | }
939 |
940 | // Find an unused id for a file being edited: return a symbol
941 | RDFIndexedFormula.prototype.nextSymbol = function(doc) {
942 | for(var i=0;;i++) {
943 | var uri = doc.uri + '#n' + i;
944 | if (!this.mentionsURI(uri)) return kb.sym(uri);
945 | }
946 | }
947 |
948 | // Find an unused id for a file being edited
949 | /* RDFIndexedFormula.prototype.newId = function(doc) {
950 | for(var i=0;;i++) {
951 | var uri = doc.uri + '#n' + i;
952 | if (!this.mentionsURI(uri)) return uri;
953 | }
954 | }
955 | */
956 |
957 | RDFIndexedFormula.prototype.anyStatementMatching = function(subj,pred,obj,why) {
958 | var x = this.statementsMatching(subj,pred,obj,why,true);
959 | if (!x || x == []) return undefined;
960 | return x[0];
961 | };
962 |
963 | // return statements matching a pattern
964 | // ALL CONVENIENCE LOOKUP FUNCTIONS RELY ON THIS!
965 | RDFIndexedFormula.prototype.statementsMatching = function(subj,pred,obj,why,justOne) {
966 | var results = [];
967 | var candidates;
968 | //tabulator.log.debug("Matching {"+subj+" "+pred+" "+obj+"}");
969 | subj = RDFMakeTerm(this, subj);
970 | pred = RDFMakeTerm(this, pred);
971 | obj = RDFMakeTerm(this, obj);
972 | why = RDFMakeTerm(this, why);
973 |
974 | if (typeof(pred) != 'undefined' && this.redirection[pred.hashString()])
975 | pred = this.redirection[pred.hashString()];
976 |
977 | if (typeof(obj) != 'undefined' && this.redirection[obj.hashString()])
978 | obj = this.redirection[obj.hashString()];
979 |
980 | //looks for candidate statements matching a given s/p/o
981 | if (typeof(subj) =='undefined') {
982 | if (typeof(obj) =='undefined') {
983 | if (typeof(pred) == 'undefined') {
984 | candidates = this.statements; //all wildcards
985 | } else {
986 | candidates = this.predicateIndex[pred.hashString()];
987 | // tabulator.log.debug("@@Trying predciate "+p+" length "+candidates.length)
988 |
989 | if (typeof candidates == 'undefined') return [];
990 | }
991 | // tabulator.log.debug("Trying all "+candidates.length+" statements")
992 | } else { // subj undefined, obj defined
993 | candidates = this.objectIndex[obj.hashString()];
994 | if (typeof candidates == 'undefined') return [];
995 | if ((typeof pred == 'undefined') &&
996 | (typeof why == 'undefined')) {
997 | // tabulator.log.debug("Returning all statements for object")
998 | return candidates ;
999 | }
1000 | // tabulator.log.debug("Trying only "+candidates.length+" object statements")
1001 | }
1002 | } else { // s defined
1003 | if (this.redirection[subj.hashString()])
1004 | subj = this.redirection[subj.hashString()];
1005 | candidates = this.subjectIndex[subj.hashString()];
1006 | if (typeof candidates == 'undefined') return [];
1007 | if (typeof(obj) =='undefined') {
1008 | if ((typeof pred == 'undefined') && (typeof why == 'undefined')) {
1009 | // tabulator.log.debug("Trying all "+candidates.length+" subject statements")
1010 | return candidates;
1011 | }
1012 | } else { // s and o defined ... unusual in practice?
1013 | var oix = this.objectIndex[obj.hashString()];
1014 | if (typeof oix == 'undefined') return [];
1015 | if (oix.length < candidates.length) {
1016 | candidates = oix;
1017 | // tabulator.log.debug("Wow! actually used object index instead of subj");
1018 | }
1019 |
1020 | }
1021 | // tabulator.log.debug("Trying only "+candidates.length+" subject statements")
1022 | }
1023 |
1024 | if (typeof candidates == 'undefined') return [];
1025 | // tabulator.log.debug("Matching {"+s+" "+p+" "+o+"} against "+n+" stmts")
1026 | var st;
1027 | for(var i=0; i -1) {
1448 | new_rels.push(XH.XHTML_PREFIX + ':' + rel_val);
1449 | add_namespaces = true;
1450 | }
1451 |
1452 | // keep the existing rel anyways, it might be used for other purposes by other libraries
1453 | new_rels.push(rel_val);
1454 | });
1455 |
1456 | // REVs
1457 | var new_revs= [];
1458 | var rev_value= RDFA.getNodeAttributeValue(element,'rev');
1459 |
1460 | RDFA.each_attr_value(rev_value, function(rev_val) {
1461 | if (XH.SPECIAL_RELS.indexOf(rev_val) > -1) {
1462 | new_revs.push(XH.XHTML_PREFIX + ':' + rev_val);
1463 | add_namespaces = true;
1464 | }
1465 |
1466 | // keep the existing rel anyways, it might be used for other purposes by other libraries
1467 | new_revs.push(rev_val);
1468 | });
1469 |
1470 | // set the new @rel
1471 | if (new_rels.length > 0)
1472 | RDFA.setNodeAttributeValue(element,'rel',new_rels.join(" "));
1473 |
1474 | // set the new @rev
1475 | if (new_revs.length > 0)
1476 | RDFA.setNodeAttributeValue(element,'rev',new_revs.join(" "));
1477 |
1478 | // add namespaces if we added RDFa rels
1479 | if (add_namespaces) {
1480 | RDFA.setNodeAttributeValue(element,'xmlns:' + XH.RDF_PREFIX, XH.RDF_URI);
1481 | RDFA.setNodeAttributeValue(element,'xmlns:' + XH.XHTML_PREFIX, XH.XHTML_URI);
1482 | }
1483 | };
1484 |
1485 | //RDFA = document.RDFA;
1486 |
1487 | //XH.transform(document.getElementsByTagName('body')[0]);
1488 | //XH.transform(document.getElementsByTagName('head')[0]);
1489 |
1490 | //RDFA.GRDDL.DONE_LOADING(document.__RDFA_BASE + 'xhtml1-hgrddl.js');
1491 |
1492 |
1493 |
1494 |
1495 |
1496 |
1497 |
1498 |
1499 |
1500 |
1501 |
1502 |
1503 | //
1504 | //
1505 | //
1506 |
1507 | RDFA.add_triple = function (base, subject, predicate, object, literal_p, literal_datatype, literal_lang) {
1508 | // 2008-01-18 JT
1509 | // changed the test here, since an empty string is a valid (and meaningful)
1510 | // URI
1511 |
1512 | if (subject == null) {
1513 | return null;
1514 | }
1515 |
1516 | if (predicate == null) {
1517 | // likely a bad CURIE
1518 | return null;
1519 | }
1520 |
1521 | // if subject is string, then create a URI
1522 | if (typeof(subject) == 'string')
1523 | subject = new RDFSymbol(Util.uri.join(subject, base));
1524 |
1525 | if (literal_p) {
1526 | object = new RDFLiteral(object, literal_lang, literal_datatype);
1527 | } else {
1528 | if (typeof(object) == 'string')
1529 | object = new RDFSymbol(Util.uri.join(object, base));
1530 | }
1531 |
1532 | return RDFA.triplestore.add(subject, predicate, object, 'RDFa');
1533 | };
1534 |
1535 | //
1536 | // Process Namespaces
1537 | //
1538 | RDFA.add_namespaces = function(element, namespaces) {
1539 | if (!namespaces)
1540 | namespaces = {};
1541 |
1542 | // we only copy the namespaces array if we really need to
1543 | var copied_yet = 0;
1544 |
1545 | // go through the attributes
1546 | var attributes = element.attributes;
1547 |
1548 | if (!attributes) {
1549 | return namespaces;
1550 | }
1551 |
1552 | for (var i=0; i") {
1774 | content = element.textContent;
1775 | if (datatype == '')
1776 | datatype = null;
1777 | } else {
1778 | content = element.innerHTML;
1779 | if (content != element.textContent)
1780 | datatype = "rdf:XMLLiteral";
1781 | }
1782 | }
1783 |
1784 | // datatype is actually a CURIE here
1785 | if (datatype != null) {
1786 | datatype = RDFA.CURIE.parse(datatype, namespaces);
1787 | }
1788 |
1789 | // go through each prop
1790 | RDFA.each_prefixed_attr_value(attrs['property'], function(prop_value) {
1791 | var property = RDFA.CURIE.parse(prop_value, namespaces);
1792 | var triple = RDFA.add_triple(base, subject, property, content, true, datatype, lang);
1793 | RDFA.CALLBACK_NEW_TRIPLE_WITH_LITERAL_OBJECT(element_to_callback, triple);
1794 | });
1795 | }
1796 |
1797 | // if we have an explicit object, we chain
1798 | if (explicit_object != null) {
1799 | RDFA.associateElementAndSubject(element, explicit_object, namespaces);
1800 | subject = explicit_object;
1801 | }
1802 |
1803 | // recurse down the children
1804 | var children = element.childNodes;
1805 | for (var i=0; i < children.length; i++) {
1806 | RDFA.traverse(children[i], subject, namespaces, lang, base, hanging);
1807 | }
1808 | };
1809 |
1810 | RDFA.parse = function(parse_document, base) {
1811 | parse_document = parse_document || document;
1812 |
1813 | parse_document.RDFA = RDFA;
1814 | RDFA.document = parse_document;
1815 | RDFA.document.__RDFA_BASE = __RDFA_BASE;
1816 |
1817 | // is there a base
1818 | if (typeof(base) != 'undefined')
1819 | RDFA.BASE = base;
1820 | else
1821 | RDFA.BASE = parse_document.location.href;
1822 |
1823 | // is it overriden by the HTML itself?
1824 | if (parse_document.getElementsByTagName('base').length > 0)
1825 | RDFA.BASE = parse_document.getElementsByTagName('base')[0].href;
1826 |
1827 | // by default, the current namespace for CURIEs is vocab#
1828 | var location = parse_document.location || document.location;
1829 | var default_ns = new Namespace('http://www.w3.org/1999/xhtml/vocab#');
1830 | var namespaces = new Object();
1831 |
1832 | // set up default namespace
1833 | namespaces[''] = default_ns;
1834 | namespaces['rdf'] = new Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
1835 |
1836 | // hGRDDL for XHTML1 special needs
1837 | // RDFA.GRDDL.addProfile(__RDFA_BASE + 'xhtml1-hgrddl.js');
1838 |
1839 | // do the profiles, and then traverse
1840 | RDFA.GRDDL.runProfiles(parse_document, function() {
1841 | // 2008-01-18 JT
1842 | // the provides the initial subject if it's given
1843 | RDFA.traverse(parse_document, RDFA.BASE, namespaces, null, RDFA.BASE, null);
1844 | //
1845 | RDFA.CALLBACK_DONE_PARSING();
1846 | });
1847 | };
1848 |
1849 | RDFA.log = function(str) {
1850 | alert(str);
1851 | };
1852 |
1853 | RDFA.CALLBACK_DONE_LOADING();
1854 |
1855 |
1856 |
1857 |
1858 |
1859 |
1860 | //RDFA.CALLBACK_DONE_PARSING = function() {
1861 | // var CC = new Namespace('http://creativecommons.org/ns#');
1862 | // var cc_license = CC('license');
1863 |
1864 | // var XHTML = new Namespace('http://www.w3.org/1999/xhtml/vocab#');
1865 | // var xhtml_license = XHTML('license');
1866 |
1867 | // var current_page = ccffext.state.rdfa.triplestore.sym(Util.uri.join('', RDFA.BASE));
1868 |
1869 | // var license = ccffext.state.rdfa.triplestore.the(current_page,cc_license);
1870 | //}
--------------------------------------------------------------------------------
/skin/browser.css:
--------------------------------------------------------------------------------
1 | /*
2 | An icon being displayed in the location bar when licensed objects are present.
3 |
4 | The icon is hidden by default. The Creative Commons small icon is shown. Additional space is added to the icon to
5 | look distinctively among other icons
6 | */
7 | image#ccffext-icon
8 | {
9 | cursor: pointer;
10 | list-style-image: url("chrome://ccffext/content/media/cc.small.png");
11 | margin: 0 3px 0 3px;
12 | }
13 |
14 | image#ccffext-popup-icon {
15 | list-style-image: url("chrome://ccffext/content/media/cc.png");
16 | }
17 |
18 | #ccffext-popup {
19 | width: 350px;
20 | }
21 |
22 | #ccffext-popup-container {
23 | padding: 10px;
24 | }
25 |
26 | #ccffext-popup-license-band {
27 | height: 25px;
28 | }
29 |
30 | .band-reset {
31 | display: none;
32 | }
33 |
34 | .band-green {
35 | display: block;
36 | background-color: green;
37 | }
38 |
39 | .band-yellow {
40 | display: block;
41 | background-color: yellow;
42 | }
43 |
44 | .band-red {
45 | display: block;
46 | background-color: red;
47 | }
48 |
49 | #ccffext-license-frame {
50 | display: none;
51 | }
52 |
--------------------------------------------------------------------------------
/skin/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/skin/icon32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/skin/icon32.png
--------------------------------------------------------------------------------
/skin/icon64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nyergler/openattribute-firefox/b5dbde9c3180fe6ebb151d4abd624411c7052297/skin/icon64.png
--------------------------------------------------------------------------------
/skin/pageInfo.css:
--------------------------------------------------------------------------------
1 | /*
2 | A tab being displayed in the "Page Info" dialog.
3 | The CC image is used as an icon
4 | */
5 | radio#ccffext-tab
6 | {
7 | list-style-image: url("chrome://ccffext/content/media/cc.png");
8 | }
9 |
10 | /*
11 | Main outer panel in the "Page Info" dialog.
12 | Should look similar the "Permissions" tab (of Fx 3.6)
13 | */
14 | vbox#ccffextPanel
15 | {
16 | padding: 0 6px 0 6px;
17 | }
18 |
19 | /*
20 | Highlight checkbox
21 | */
22 | checkbox#ccffext-highlight
23 | {
24 | margin: 0;
25 | margin-top: -2px;
26 | -moz-margin-end: 3px;
27 | }
28 |
29 | /*
30 | List of licensed objects
31 | */
32 | vbox#ccffext-objects
33 | {
34 | margin-top: 5px;
35 | margin-bottom: 0px;
36 | -moz-appearance: listbox;
37 | }
38 |
39 | /*
40 | Licensed object item
41 | */
42 | vbox#ccffext-objects .item
43 | {
44 | border-bottom: dotted 1px #c0c0c0;
45 | padding: 6px 8px 6px 8px;
46 | }
47 |
48 | /*
49 | Title of licensed object item
50 | */
51 | vbox#ccffext-objects .item label.title
52 | {
53 | font-weight: bold;
54 | }
55 |
56 | /*
57 | A line of information of licensed object item
58 | */
59 | vbox#ccffext-objects .item .primary
60 | {
61 | margin-top: 6px;
62 | }
63 | vbox#ccffext-objects .item .indented
64 | {
65 | margin-left: 1em;
66 | }
67 |
68 | /*
69 | A line of information of licensed object item
70 | */
71 | vbox#ccffext-objects .item .line label.line-title
72 | {
73 | width: 100px;
74 | }
75 |
76 | vbox#ccffext-objects .item textbox
77 | {
78 | border: 1px solid #232;
79 | padding: 0.25em;
80 | }
81 |
82 | /*
83 | A label that looks like an anchor element
84 | */
85 | label.anchor
86 | {
87 | color: #0000ff;
88 | cursor: pointer;
89 | text-decoration: underline;
90 | }
91 |
92 | button {
93 | -moz-box-align: center;
94 | -moz-box-pack: start;
95 | }
96 |
97 | .aligncenter {
98 | -moz-box-align: center;
99 | }
100 |
--------------------------------------------------------------------------------
/xpi.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Remove old *.xpi file
4 | rm *.xpi
5 |
6 | # Pack all extension files
7 | zip -9 -r `basename \`pwd\``.xpi content locale module skin defaults chrome.manifest install.rdf icon.png
8 |
--------------------------------------------------------------------------------