├── .gitignore
├── .idea
├── inspectionProfiles
│ ├── Project_Default.xml
│ └── profiles_settings.xml
├── istex-web-extension.iml
├── modules.xml
└── vcs.xml
├── .jshintrc
├── LICENSE
├── Makefile
├── README.md
├── package.json
├── src
├── .jshintrc
├── .web-extension-id
├── background
│ ├── postInstall.js
│ └── tabBootstrap.js
├── content_scripts
│ ├── gs-pref.js
│ ├── log.js
│ ├── main.css
│ ├── main.js
│ ├── storage.js
│ └── tabBootstrap.js
├── icons
│ ├── icon-48.png
│ ├── istex-128.png
│ ├── istex-128.xcf
│ ├── istex-16.png
│ └── istex-48.png
├── manifest.json
├── options
│ ├── options.css
│ ├── options.html
│ └── options.js
└── vendors
│ ├── jquery-3.2.1.js
│ └── lz-string.js
├── test
└── index.html
└── web-ext-artifacts
└── istex-1.2.7.zip
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/node,webstorm,vim,netbeans,linux
3 |
4 | ### Node ###
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 |
10 | # Runtime data
11 | pids
12 | *.pid
13 | *.seed
14 | *.pid.lock
15 |
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 |
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 |
22 | # nyc test coverage
23 | .nyc_output
24 |
25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26 | .grunt
27 |
28 | # node-waf configuration
29 | .lock-wscript
30 |
31 | # Compiled binary addons (http://nodejs.org/api/addons.html)
32 | build/Release
33 |
34 | # Dependency directories
35 | node_modules
36 | jspm_packages
37 |
38 | # Optional npm cache directory
39 | .npm
40 |
41 | # Optional eslint cache
42 | .eslintcache
43 |
44 | # Optional REPL history
45 | .node_repl_history
46 |
47 | # Output of 'npm pack'
48 | *.tgz
49 |
50 |
51 | ### WebStorm ###
52 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
53 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
54 | .idea/
55 |
56 | # User-specific stuff:
57 | .idea/workspace.xml
58 | .idea/tasks.xml
59 |
60 | # Sensitive or high-churn files:
61 | .idea/dataSources.ids
62 | .idea/dataSources.xml
63 | .idea/dataSources.local.xml
64 | .idea/sqlDataSources.xml
65 | .idea/dynamic.xml
66 | .idea/uiDesigner.xml
67 |
68 | # Gradle:
69 | .idea/gradle.xml
70 | .idea/libraries
71 |
72 | # Mongo Explorer plugin:
73 | .idea/mongoSettings.xml
74 |
75 | ## File-based project format:
76 | *.iws
77 |
78 | ## Plugin-specific files:
79 |
80 | # IntelliJ
81 | /out/
82 |
83 | # mpeltonen/sbt-idea plugin
84 | .idea_modules/
85 |
86 | # JIRA plugin
87 | atlassian-ide-plugin.xml
88 |
89 | # Crashlytics plugin (for Android Studio and IntelliJ)
90 | com_crashlytics_export_strings.xml
91 | crashlytics.properties
92 | crashlytics-build.properties
93 | fabric.properties
94 |
95 | ### WebStorm Patch ###
96 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
97 |
98 | # *.iml
99 | # modules.xml
100 | # .idea/misc.xml
101 | # *.ipr
102 |
103 |
104 | ### Vim ###
105 | # swap
106 | [._]*.s[a-w][a-z]
107 | [._]s[a-w][a-z]
108 | # session
109 | Session.vim
110 | # temporary
111 | .netrwhist
112 | *~
113 | # auto-generated tag files
114 | tags
115 |
116 |
117 | ### NetBeans ###
118 | nbproject/private/
119 | build/
120 | nbbuild/
121 | nbdist/
122 | .nb-gradle/
123 |
124 |
125 | ### Linux ###
126 |
127 | # temporary files which can be created if a process still has a handle open of a deleted file
128 | .fuse_hidden*
129 |
130 | # KDE directory preferences
131 | .directory
132 |
133 | # Linux trash folder which might appear on any partition or disk
134 | .Trash-*
135 |
136 | # .nfs files are created when an open file is removed but is still being accessed
137 | .nfs*
138 |
139 | ### web extension release ###
140 | *.pem
141 | dist/
142 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/istex-web-extension.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "laxbreak": true,
3 | "eqeqeq": true,
4 | "expr": true,
5 | "quotmark": true,
6 | "indent": 2,
7 | "undef": true,
8 | "unused": true,
9 | "strict": true,
10 | "shadow": "outer",
11 | "esnext": true,
12 | "validthis": true,
13 | "node": true,
14 | "no-cond-assign": false
15 | }
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Développements ISTEX en open source
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # istex-web-extension's Makefile
2 |
3 | SHELL:=/bin/bash
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **⚠⚠⚠ This extension is deprecated and no more maintained. Please consider replacing it by [Click & Read](https://clickandread.inist.fr/)⚠⚠⚠**
2 |
3 | ---
4 |
5 | # istex-web-extension
6 |
7 | A basic add-on for identifying dynamically ISTEX resources in the browser pages.
8 |
9 | This extension is an adaptation of [istex-browser-addon](https://github.com/istex/istex-browser-addon), using "Web extensions" technology.
10 |
11 | At the present time, 2 versions are available : one for Mozilla Firefox, the other for Google Chrome.
12 |
13 | ## Functionalities
14 |
15 | This add-on performs the following task:
16 |
17 | * Add an ISTEX button next to any DOI, OpenUrl and PMID found in the browser page in case the corresponding document is present in ISTEX, based on the ISTEX OpenURL service. Clicking on the ISTEX button will open a new tab with opening the corresponding PDF, assuming that the access to the ISTEX full-texts is authorized.
18 |
19 | ## Supported identifiers and protocols
20 |
21 | Linking work at item level (e.g. article) and will try to identifying the following identifiers in the web page:
22 |
23 | * OpenURL 1.0, including COInS - link resolver prefixes will be examined in case of SFX and Proquest 360 Link
24 | * DOI
25 | * PubMed ID (PMID)
26 | * Publisher Item Identifier (PII)
27 |
28 | ## Supported browser
29 |
30 | Currently:
31 |
32 | * Firefox
33 | * Chrome
34 |
35 | ## Examples
36 |
37 | * Example of links on a Wikipedia page: https://en.wikipedia.org/wiki/Superfluid_helium-4
38 |
39 | ## How to install
40 |
41 | If you just want to install the extension, please visit https://addons.istex.fr and click on the big "Install" button.
42 |
43 | If you use Google Chrome, you can alose visit the [extension's homepage on the Chrome Web Store](https://chrome.google.com/webstore/detail/istex/fonjnfcanlbgnjgfhiocggldmpnhdhjg?hl=fr) and click on the "Add to Chrome" button.
44 |
45 | ## Developers
46 |
47 | How to build the xpi:
48 | ```
49 | npm i
50 | npm run build
51 | ```
52 |
53 | How to run the web extension in developer mode with firefox (you need to install firefox >= 49):
54 | ```
55 | npm i
56 | npm run run
57 | ```
58 | It will open firefox on this page https://en.wikipedia.org/wiki/Superfluid_helium-4 with the istex-web-extension loaded.
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "istex-web-extension",
3 | "version": "1.3.1",
4 | "description": "An add-on for identifying ISTEX resources in the browser pages",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "build": "web-ext build -s ./src -a ./web-ext-artifacts",
9 | "run": "web-ext run -s ./src -a ./web-ext-artifacts --browser-console --start-url https://en.wikipedia.org/wiki/Superfluid_helium-4 --start-url 'https://scholar.google.fr/scholar?q=elsevier+brain&btnG=&hl=fr&as_sdt=0%2C5&as_ylo=2001&as_yhi=2001'"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/istex/istex-web-extension.git"
14 | },
15 | "keywords": [
16 | "Istex",
17 | "web-extension"
18 | ],
19 | "author": "Istex",
20 | "license": "MIT",
21 | "bugs": {
22 | "url": "https://github.com/istex/istex-web-extension/issues"
23 | },
24 | "homepage": "https://github.com/istex/istex-web-extension#readme",
25 | "dependencies": {},
26 | "devDependencies": {
27 | "web-ext": "^1.10.1"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../.jshintrc",
3 | "browser": true,
4 | "jquery": true,
5 | "strict": true,
6 | "globals": {
7 | "browser": false,
8 | "chrome": false,
9 | "Storage": false,
10 | "DOMException": false,
11 | "config": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/.web-extension-id:
--------------------------------------------------------------------------------
1 | # This file was created by https://github.com/mozilla/web-ext
2 | # Your auto-generated extension ID for addons.mozilla.org is:
3 | istexwebextensionforreal@inist.fr
--------------------------------------------------------------------------------
/src/background/postInstall.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function handleInstalled(details) {
4 | console.log(details.reason);
5 | if (details.reason === 'install') {
6 | chrome.tabs.create({
7 | url: chrome.runtime.getURL('/options/options.html')
8 | });
9 | }
10 | }
11 |
12 | chrome.runtime.onInstalled.addListener(handleInstalled);
--------------------------------------------------------------------------------
/src/background/tabBootstrap.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var whiteList = [
3 | 'scholar.google.*',
4 | '*.wikipedia.org',
5 | 'scholar.*.fr',
6 | '*' // Until we get better and/or configurable whitelist
7 | ],
8 | whiteListPatterns = whiteList.map(compileUrlPattern)
9 | ;
10 |
11 |
12 | chrome.runtime.onConnect.addListener(function(port) {
13 | port.onMessage.addListener(function(page) {
14 | if (!isContentTypeAllowed(page.contentType)
15 | || !isWhiteListed(port.sender.url)
16 | ) return;
17 |
18 | chrome.tabs.executeScript(port.sender.tab.id, {file: '/vendors/jquery-3.2.1.js'});
19 | chrome.tabs.executeScript(port.sender.tab.id, {file: '/vendors/lz-string.js'});
20 | chrome.tabs.executeScript(port.sender.tab.id, {file: '/content_scripts/log.js'});
21 | chrome.tabs.executeScript(port.sender.tab.id, {file: '/content_scripts/storage.js'});
22 | chrome.tabs.executeScript(port.sender.tab.id, {file: '/content_scripts/main.js'});
23 | });
24 | });
25 |
26 | function isContentTypeAllowed (contentType) {
27 | var forbidenContentTypes = [
28 | /application\/(\w+\+)?xml/,
29 | /text\/xml/,
30 | ];
31 |
32 | return !forbidenContentTypes.find(function(regex) {
33 | return contentType.match(regex);
34 | });
35 |
36 | }
37 |
38 | function isWhiteListed (url) {
39 | for (var i = 0; i < whiteListPatterns.length; ++i) {
40 | if (url.match(whiteListPatterns[i])) {
41 | return true;
42 | }
43 | }
44 | return false;
45 | }
46 |
47 | function escapeStringForRegex (str) {
48 | return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
49 | }
50 |
51 | function compileUrlPattern (url) {
52 | return new RegExp(
53 | escapeStringForRegex(url).replace('\\*', '.*'),
54 | 'i'
55 | );
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/src/content_scripts/gs-pref.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | window.onunload = function(e) {
4 | console.log('unload event');
5 | chrome.runtime.sendMessage({text: 'done'});
6 | };
7 |
8 | // we need to send a click to this
9 | //
10 |
11 | $('.gs_btn_act').click();
12 |
--------------------------------------------------------------------------------
/src/content_scripts/log.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function log (message) {
4 | if (!console || !console.log) return;
5 | if (isObject(message) && console.dir && arguments.length === 1) {
6 | console.dir(message);
7 | return;
8 | }
9 | console.log.apply(null, arguments);
10 | }
11 |
12 | function trace (message) {
13 | log.apply(null, arguments);
14 |
15 | if (console.trace) {
16 | console.trace('\n\n');
17 | console.log('\n\n');
18 | } else {
19 | console.log('%c' + (new Error().stack || '').split('\n').slice(1).join('\n') + '\n\n', 'color: #9e9ea6');
20 | }
21 | }
22 |
23 | function info () {
24 | var args = Array.prototype.slice.call(arguments);
25 | args.unshift('%cistex-web-extension: %c%s', 'color:#337ab7;font-weight:bold;', 'color: #51515d;');
26 | console.info.apply(null, args);
27 | }
28 |
29 | function warn (message) {
30 | if (!console || !console.warn) return log(message);
31 | console.warn.apply(null, arguments);
32 | }
33 |
34 | function error () {
35 | if (!(config && config.mustDebug)) return;
36 | if (!console || !console.error) return log.apply(null, arguments);
37 | console.error.apply(null, arguments);
38 | }
39 |
40 | function debug (message) {
41 | if (!(config && config.mustDebug)) return;
42 | warn('istex-web-extension: ' + message);
43 | }
44 |
45 | function logXhrError (url, statusText) {
46 | console.error('%cistex-web-extension:%c %s %c %s',
47 | 'color:red;font-weight:bold;',
48 | 'color: #51515d;',
49 | url,
50 | 'color:red;font-weight:bold;',
51 | statusText);
52 | }
53 | function isObject (value) {
54 | return value && 'object' === typeof value || 'function' === typeof value;
55 | }
56 |
--------------------------------------------------------------------------------
/src/content_scripts/main.css:
--------------------------------------------------------------------------------
1 | a[name="ISTEXLink"].istex-link {
2 | margin: 2px 5px 2px 5px;
3 | padding: 2px;
4 | width: 60px;
5 | border: 1px solid;
6 | border-top-color: #ccc;
7 | border-left-color: #ccc;
8 | border-bottom-color: #000;
9 | border-right-color: #000;
10 | background: #458ca5 !important;
11 | text-align: center;
12 | text-decoration: none !important;
13 | font: normal 10px Verdana;
14 | color: white !important;
15 | font-weight: bold;
16 | /* border-radius: 5px;*/
17 | }
18 |
19 | [class^="gs"] > a[name="ISTEXLink"] {
20 | margin-left: 0;
21 | }
22 |
23 | a[name="ISTEXLink"].istex-link:hover {
24 | text-decoration: none !important;
25 | color: white !important;
26 | background: #c4d733 !important;
27 | font-weight: bold;
28 | }
29 |
30 | a[name="ISTEXLink"].istex-link:active {
31 | border-top-color: #000;
32 | border-left-color: #000;
33 | border-bottom-color: #ccc;
34 | border-right-color: #ccc;
35 | background: #666;
36 | text-decoration: none !important;
37 | color: white;
38 | font-weight: bold;
39 | }
40 |
--------------------------------------------------------------------------------
/src/content_scripts/main.js:
--------------------------------------------------------------------------------
1 | /* globals LZString, warn, error, debug, logXhrError */
2 | 'use strict';
3 |
4 | var config,
5 | ISTEXLinkInserter,
6 | NOT_AVAILABLE = 'NA'
7 | ;
8 | const forbidenElements = ['applet',
9 | 'area',
10 | 'audio',
11 | 'br',
12 | 'canvas',
13 | 'center',
14 | 'embed',
15 | 'frame',
16 | 'frameset',
17 | 'hr',
18 | 'iframe',
19 | 'img',
20 | 'input',
21 | 'keygen',
22 | 'link',
23 | 'map',
24 | 'meta',
25 | 'meter',
26 | 'noframes',
27 | 'noscript',
28 | 'object',
29 | 'optgroup',
30 | 'option',
31 | 'output',
32 | 'param',
33 | 'picture',
34 | 'progress',
35 | 'script',
36 | 'select',
37 | 'source',
38 | 'textarea',
39 | 'time',
40 | 'track',
41 | 'video',
42 | 'wbr',
43 | 'svg',
44 | 'g',
45 | 'path',
46 | 'text',
47 | 'rect',
48 | 'style'];
49 |
50 | config = {
51 | istexBaseURL: 'api.istex.fr/document/openurl',
52 | maxPageLinks: 2500,
53 | mustDebug : false
54 | };
55 |
56 | ISTEXLinkInserter = {
57 | // OpenURL static info
58 | openUrlVersion: 'Z39.88-2004',
59 | openURLPrefix : 'https://api.istex.fr/document/openurl?',
60 |
61 | // DOI pattern
62 | doiPattern : /\/\/((dx\.)?doi\.org|doi\.acm\.org|dx\.crossref\.org).*\/(10\..*(\/|%2(F|f)).*)/,
63 | // the index of the group where to find the DOI
64 | doiGroup : 3,
65 | regexDoiPatternConservative: new RegExp('(10\\.\\d{4,5}\\/[\\S]+[^;,.\\s])', 'gi'),
66 |
67 | // PMID
68 | pubmedPattern : new RegExp('http.*\\/\\/.*ncbi\\.nlm\\.nih\\.gov.*\\/pubmed.*(\\/|=)([0-9]{4,12})', 'i'),
69 | pubmedGroup : 1,
70 | regexPMIDPattern : new RegExp('(PubMed\\s?(ID\\s?:?|:)|PM\\s?ID)[\\s:\\/]?\\s*([0-9]{4,12})', 'gi'),
71 | regexPrefixPMIDPattern: new RegExp('((PubMed\\s?(ID)?:?)|(PM\\s?ID))[\\s:\\/]*$', 'i'),
72 | regexSuffixPMIDPattern: new RegExp('^\\s*[:\\/]?\\s*([0-9]{4,12})', 'i'),
73 | skipPattern : new RegExp('^[:\\/\\s]+$', 'i'),
74 |
75 | // PII pattern in links
76 | regexPIIPattern: new RegExp('\\pii\\/([A-Z0-9]{16,20})', 'gi'),
77 |
78 | // The last group should be the parameters for openurl resolver - TBD add EBSCO
79 | openUrlPattern: /.*(sfxhosted|sfx?|search|.hosted).(exlibrisgroup|serialssolutions).com.*(\/|%2(F|f))?\?*(.*)/,
80 | flags : {
81 | OPEN_URL_BASE : 1,
82 | DOI_ADDRESS : 2,
83 | PUBMED_ADDRESS : 3,
84 | HAS_OPEN_URL : 4,
85 | HAS_PII : 5,
86 | GOOGLE_SCHOLAR_OPENURL: 6,
87 | SCOPUS_DOI : 7
88 | },
89 |
90 | scopusExternalLinkPrefix: 'www.scopus.com/redirect/linking.uri?targetURL=',
91 |
92 | onDOMContentLoaded: function() {
93 | var rootElement = document.documentElement;
94 | // check if we have an html page
95 | debug(document.contentType);
96 | if (document.contentType === 'text/html') {
97 | var currentUrl = window.location.href;
98 | if (currentUrl.indexOf('grobid') === -1) {
99 | ISTEXLinkInserter.findAndReplaceLinks(rootElement);
100 | rootElement.addEventListener('DOMNodeInserted', ISTEXLinkInserter.onDOMNodeInserted, false);
101 | }
102 | }
103 |
104 | },
105 |
106 | onDOMNodeInserted: function(event) {
107 | var node = event.target;
108 | ISTEXLinkInserter.findAndReplaceLinks(node);
109 | },
110 |
111 | scanForDoiAndPubmedStrings: function(domNode, prefixStatus) {
112 | var prefix = prefixStatus;
113 | // Only process valid dom nodes:
114 | if (domNode === null || !domNode.getElementsByTagName) {
115 | return prefix;
116 | }
117 |
118 | if (forbidenElements.includes(domNode.tagName.toLowerCase())) return false;
119 |
120 | // if the node is already clickable
121 | if (domNode.tagName.toLowerCase() === 'a') {
122 | return false;
123 | }
124 |
125 | var childNodes = domNode.childNodes,
126 | childNode,
127 | spanElm,
128 | i = 0,
129 | text
130 | ;
131 |
132 | while ((childNode = childNodes[i])) {
133 | if (childNode.nodeType === 3) { // text node found, do the replacement
134 | text = childNode.textContent;
135 | if (text) {
136 | var matchDOI = text.match(this.regexDoiPatternConservative);
137 | var matchPMID = text.match(this.regexPMIDPattern);
138 | if (matchDOI || matchPMID) {
139 | spanElm = document.createElement('span');
140 | spanElm.setAttribute('name', 'ISTEXInserted');
141 |
142 | if (matchDOI) {
143 | spanElm.innerHTML = text.replace(this.regexDoiPatternConservative,
144 | '$1');
145 | text = spanElm.innerHTML;
146 | }
147 | if (matchPMID) {
148 | spanElm.innerHTML =
149 | text
150 | .replace(this.regexPMIDPattern,
151 | 'PubMed ID $3'
152 | );
153 | }
154 | domNode.replaceChild(spanElm, childNode);
155 | childNode = spanElm;
156 | text = spanElm.innerHTML;
157 | prefix = false;
158 | }
159 | else {
160 | if (prefix && (text.match(this.regexSuffixPMIDPattern))) {
161 | debug('regexSuffixPMIDPattern: ' + text);
162 | spanElm = document.createElement('span');
163 | spanElm.setAttribute('name', 'ISTEXInserted');
164 | spanElm.innerHTML = text.replace(this.regexSuffixPMIDPattern,
165 | '$1');
166 | domNode.replaceChild(spanElm, childNode);
167 | childNode = spanElm;
168 | text = spanElm.innerHTML;
169 | prefix = false;
170 | }
171 | else if (text.match(this.regexPrefixPMIDPattern)) {
172 | debug('regexPrefixPMIDPattern: ' + text);
173 | prefix = true;
174 | }
175 | else if (text.length > 0) {
176 | if (!text.match(this.skipPattern)) {
177 | prefix = false;
178 | }
179 | }
180 | }
181 | }
182 | }
183 | else if (childNode.nodeType === 1) { // not a text node but an element node, we look forward
184 | prefix = this.scanForDoiAndPubmedStrings(childNode, prefix);
185 | }
186 | i++;
187 | }
188 | return prefix;
189 | },
190 |
191 | findAndReplaceLinks: function(domNode) {
192 | // Only process valid domNodes:
193 | if (!domNode || !domNode.getElementsByTagName) return;
194 |
195 | this.scanForDoiAndPubmedStrings(domNode, false);
196 |
197 | // Detect OpenURL, DOI or PII links not already handled in the code above and replace them with our custom links
198 | var links = domNode.getElementsByTagName('a');
199 |
200 | if (links.length > config.maxPageLinks) {
201 | warn('Too many links for ISTEX analyser:' + links.length);
202 | return;
203 | }
204 |
205 | for (var i = 0; i < links.length; i++) {
206 | var link = links[i];
207 | var flags = this.analyzeLink(link);
208 |
209 | if (flags === 0) {
210 | continue;
211 | }
212 |
213 | var href = decodeURIComponent(link.getAttribute('href'));
214 |
215 | // We have found an open url link:
216 | if (flags === this.flags.HAS_OPEN_URL) {
217 | // OpenURl
218 | this.createOpenUrlLink(href, link);
219 | }
220 | else if (flags === this.flags.DOI_ADDRESS) {
221 | // doi
222 | this.createDoiLink(href, link);
223 | }
224 | else if (flags === this.flags.GOOGLE_SCHOLAR_OPENURL) {
225 | this.createGoogleScholarLink(href, link);
226 | }
227 | else if (flags === this.flags.PUBMED_ADDRESS) {
228 | // PubMed ID
229 | this.createPubmedLink(href, link);
230 | }
231 | else if (flags === this.flags.HAS_PII) {
232 | // Publisher Item Identifier
233 | this.createPIILink(href, link);
234 | } else if (flags === this.flags.SCOPUS_DOI) {
235 | // scopus external publisher link
236 | this.createScopusLink(href, link);
237 | }
238 |
239 | }
240 |
241 | this.createSpanBasedLinks(domNode);
242 | },
243 |
244 | analyzeLink: function(link) {
245 | // First check if we have to bother:
246 | var mask = 0;
247 |
248 | if (!link.getAttribute('href')) {
249 | return mask;
250 | }
251 |
252 | var href = link.getAttribute('href');
253 | var currentUrl = window.location.href;
254 | if (link.getAttribute('name') === 'ISTEXVisited') {
255 | return mask;
256 | }
257 | if (link.getAttribute('classname') === 'istex-link') {
258 | return mask;
259 | }
260 | if (href.indexOf(config.istexBaseURL) !== -1) {
261 | return mask;
262 | }
263 |
264 | // check if we have a Google Scholar pre-OpenURL link (the link that will call the OpenURL)
265 | var contentText = link.textContent;
266 | if (href.indexOf('scholar.google.') !== -1 && (contentText === '[PDF] ISTEX')) {
267 | mask = this.flags.GOOGLE_SCHOLAR_OPENURL;
268 | //return mask;
269 | } else if (href.indexOf(this.scopusExternalLinkPrefix) !== -1) {
270 | // check scopus external publisher links
271 | var simpleHref = href.replace('https://' + this.scopusExternalLinkPrefix, '');
272 | simpleHref = decodeURIComponent(simpleHref);
273 | var ind = simpleHref.indexOf('&');
274 | if (ind !== -1)
275 | simpleHref = simpleHref.substring(0, ind);
276 | if (simpleHref.match(this.doiPattern)) {
277 | mask = this.flags.SCOPUS_DOI;
278 | }
279 | } else if ((href.indexOf('doi.org') !== -1 ||
280 | href.indexOf('doi.acm.org') !== -1 ||
281 | href.indexOf('dx.crossref.org') !== -1)
282 | && href.match(this.doiPattern)) {
283 | // Check if the href contains a DOI link
284 | mask = this.flags.DOI_ADDRESS;
285 | } else if (href.indexOf('ncbi.nlm.nih.gov') !== -1 && this.pubmedPattern.test(href)) {
286 | // Check if the href contains a PMID link
287 | mask = this.flags.PUBMED_ADDRESS;
288 | } else if (this.regexPIIPattern.test(href) && currentUrl.indexOf('scholar.google.') === -1) {
289 | // Check if the href contains a PII link
290 | mask = this.flags.HAS_PII;
291 | } else if (href.indexOf('exlibrisgroup.com') !== -1 && this.openUrlPattern.test(href)) {
292 | // Check if the href contains a supported reference to an open url link
293 | mask = this.flags.OPEN_URL_BASE;
294 | } else if (href.indexOf('serialssolutions.com') !== -1 && this.openUrlPattern.test(href)) {
295 | if (link.getAttribute('class') !== 'documentLink') {
296 | mask = this.flags.OPEN_URL_BASE;
297 | }
298 | }
299 |
300 | if (config.mustDebug && mask > 0) {
301 | debug('URL is ' + href + '\n mask value: ' + mask);
302 | }
303 |
304 | return mask;
305 | },
306 |
307 | createOpenUrlLink: function(href, link) {
308 | var matchInfo = this.openUrlPattern.exec(href);
309 | if (!matchInfo) return;
310 | // the last group should be the parameters:
311 | var child = this.buildButton(matchInfo[matchInfo.length - 1]);
312 | link.parentNode.replaceChild(child, link);
313 | },
314 |
315 | createDoiLink: function(href, link) {
316 | var matchInfo = this.doiPattern.exec(href);
317 | if (matchInfo.length < this.doiGroup) {
318 | return;
319 | }
320 | var doiString = matchInfo[this.doiGroup];
321 | var istexUrl = 'rft_id=info:doi/' + doiString;
322 | var newLink = this.buildButton(istexUrl);
323 | link.parentNode.insertBefore(newLink, link.nextSibling);
324 | link.setAttribute('name', 'ISTEXVisited');
325 | },
326 |
327 | createScopusLink: function(href, link) {
328 | var simpleHref = href.replace('https://' + this.scopusExternalLinkPrefix, '');
329 | simpleHref = decodeURIComponent(simpleHref);
330 | var ind = simpleHref.indexOf('&');
331 | if (ind !== -1)
332 | simpleHref = simpleHref.substring(0, ind);
333 |
334 | var matchInfo = this.doiPattern.exec(simpleHref);
335 | if (matchInfo.length < this.doiGroup) {
336 | return;
337 | }
338 | var doiString = matchInfo[this.doiGroup];
339 | var istexUrl = 'rft_id=info:doi/' + doiString;
340 | var newLink = this.buildButton(istexUrl);
341 | newLink.setAttribute('style', 'visibility:visible;');
342 | link.parentNode.insertBefore(newLink, link.nextSibling);
343 | link.setAttribute('name', 'ISTEXVisited');
344 | },
345 |
346 | createPubmedLink: function(href, link) {
347 | var istexUrl =
348 | href.replace(
349 | this.pubmedPattern,
350 | 'rft_id=info:pmid/$2&rft.genre=article,chapter,bookitem&svc.fulltext=yes'
351 | );
352 | var newLink = this.buildButton(istexUrl);
353 | link.parentNode.insertBefore(newLink, link.nextSibling);
354 | link.setAttribute('name', 'ISTEXVisited');
355 | },
356 |
357 | createPIILink: function(href, link) {
358 | var matches = href.match(this.regexPIIPattern);
359 | if (matches && (matches.length > 0)) {
360 | var istexUrl = 'rft_id=info:' + matches[0] + '&rft.genre=article,chapter,bookitem&svc.fulltext=yes';
361 | var newLink = this.buildButton(istexUrl);
362 | link.parentNode.insertBefore(newLink, link.nextSibling);
363 | link.setAttribute('name', 'ISTEXVisited');
364 | }
365 | },
366 |
367 | createGoogleScholarLink: function(href, link) {
368 | // we simply make the ISTEX button with the existing google scholar url (which will call the ISTEX OpenURL service)
369 | link.textContent = 'ISTEX';
370 | link.name = 'ISTEXLink';
371 | link.className = 'istex-link';
372 | link.target = '_blank';
373 | //link.setAttribute('name', 'ISTEXVisited');
374 | },
375 |
376 | // Wikipedia for instance is using COInS spans
377 | createSpanBasedLinks: function(doc) {
378 | // Detect latent OpenURL SPANS and replace them with ISTEX links
379 | var spans = doc.getElementsByTagName('span');
380 | for (var i = 0, n = spans.length; i < n; i++) {
381 | var span = spans[i];
382 | var query = span.getAttribute('title');
383 |
384 | // /Z3988 means OpenURL
385 | var clazzes = span.getAttribute('class') === null ? '' : span.getAttribute('class');
386 | var name = span.getAttribute('name') === null ? '' : span.getAttribute('name');
387 |
388 | if ((name !== 'ISTEXVisited') && (clazzes.match(/Z3988/i) !== null)) {
389 | query += '&url_ver=' + ISTEXLinkInserter.openUrlVersion;
390 | var child = this.buildButton(query);
391 | span.appendChild(child);
392 | span.setAttribute('name', 'ISTEXVisited');
393 | }
394 |
395 |
396 | }
397 | },
398 | /**
399 | * Make the ISTEX button.
400 | *
401 | * @param {Object} href
402 | */
403 | buildButton : function(href) {
404 | debug('making link: ' + this.openURLPrefix + href + '&noredirect&sid=istex-browser-addon');
405 |
406 | var span = document.createElement('span');
407 | this.makeChild(href, document, span);
408 | return span;
409 | },
410 |
411 | createLink: function(resourceUrl) {
412 | // set the added link, this will avoid an extra call to the OpenURL API and fix the access url
413 | var a = document.createElement('a');
414 | a.href = resourceUrl.replace('/original', '/pdf')
415 | a.target = '_blank';
416 | a.alt = 'ISTEX';
417 | a.name = 'ISTEXLink';
418 | a.className = 'istex-link';
419 | a.textContent = 'ISTEX';
420 |
421 | return a;
422 | },
423 |
424 | makeChild: function(href, document, parent) {
425 | var key = LZString.compress(href),
426 | resourceUrl
427 | ;
428 |
429 | // insert the sid in the openurl for usage statistics reason
430 | if (!~href.indexOf('sid=')) {
431 | // sid is alone in the given openurl
432 | href += '&sid=istex-browser-addon';
433 | } else {
434 | // sid is not alone in the given openurl
435 | // then we have to handle special case if
436 | // the sid value is empty
437 | // (ex: ?foo=bar&sid= or ?sid=&foo=bar)
438 | if (/sid=(&|$)/.test(href)) {
439 | href = href.replace('sid=', 'sid=istex-browser-addon');
440 | } else {
441 | href = href.replace('sid=', 'sid=istex-browser-addon,');
442 | }
443 | }
444 |
445 | var sid = this.parseQuery(href).sid;
446 |
447 | if ((resourceUrl = localStorage.getItem(key))) {
448 | if (resourceUrl === NOT_AVAILABLE) {
449 | parent = null;
450 | return;
451 | }
452 | parent
453 | .appendChild(
454 | ISTEXLinkInserter.createLink(resourceUrl)
455 | );
456 | return;
457 | }
458 |
459 | var requestUrl = ISTEXLinkInserter.openURLPrefix + href + '&noredirect'
460 | ;
461 |
462 | $.ajax(
463 | {
464 | url : requestUrl,
465 | timeout : 16000,
466 | tryCount: 0,
467 | maxRetry: 1,
468 | success : function(data) {
469 | parent
470 | && parent.appendChild(
471 | ISTEXLinkInserter.createLink(data.resourceUrl)
472 | );
473 | },
474 | error : function(jqXHR, textStatus, errorThrown) {
475 | logXhrError(requestUrl, errorThrown);
476 | if (
477 | textStatus === 'timeout'
478 | && this.tryCount < this.maxRetry
479 | ) {
480 | info('Retry: ', this.url);
481 | this.tryCount++;
482 | return $.ajax(this);
483 | }
484 | // Todo fix the async behavior using callback style for element creation
485 | parent && parent.parentNode && parent.parentNode.removeChild(parent);
486 | },
487 | complete: function(jqXHR) {
488 | if (~[200, 300, 404].indexOf(jqXHR.status)) {
489 | if (!localStorage.getLastRefresh()) {
490 | localStorage.setLastRefresh();
491 | }
492 | localStorage.setItemOrClear(key, jqXHR.responseJSON.resourceUrl || NOT_AVAILABLE);
493 | }
494 |
495 |
496 | }
497 | }
498 | );
499 | },
500 |
501 | /**
502 | * To parse the querystring
503 | * (used for extracting sid value)
504 | */
505 | parseQuery: function(qstr) {
506 | var query = {},
507 | paires = qstr.substring(1).split('&'),
508 | paire
509 | ;
510 | for (var i = 0; i < paires.length; i++) {
511 | paire = paires[i].split('=');
512 | try {
513 | query[decodeURIComponent(paire[0])]
514 | = decodeURIComponent(paire[1] || '');
515 | } catch (err) {
516 | error(err);
517 | }
518 | }
519 | return query;
520 | }
521 |
522 | };
523 |
524 | // We need to remove trace of the old way refresh Timestamp
525 | if (localStorage.getItem('last-refesh')) {
526 | localStorage.clear();
527 | }
528 |
529 | if (!localStorage.getLastRefresh()) {
530 | setTimeout(ISTEXLinkInserter.onDOMContentLoaded, 0);
531 | } else {
532 | info('Check data freshness');
533 | $.ajax(
534 | {
535 | url : 'https://api.istex.fr/properties',
536 | timeout : 5000,
537 | tryCount: 0,
538 | maxRetry: 1,
539 | success : function(data) {
540 | if (data.corpus.lastUpdate > localStorage.getLastRefresh()) {
541 | localStorage.refresh();
542 | }
543 | ISTEXLinkInserter.onDOMContentLoaded();
544 | },
545 | error : function(jqXHR, textStatus, errorThrown) {
546 | error(textStatus, errorThrown);
547 | if (textStatus === 'timeout' && this.tryCount < this.maxRetry) {
548 | info('Retry: ', this.url);
549 | this.tryCount++;
550 | return $.ajax(this);
551 | }
552 | ISTEXLinkInserter.onDOMContentLoaded();
553 | }
554 | });
555 | }
556 |
557 |
558 |
559 |
--------------------------------------------------------------------------------
/src/content_scripts/storage.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 | var LAST_REFRESH = 'istex-last-refresh'
4 | ;
5 |
6 | /**
7 | * nettoie Storage si la donnée la plus ancienne à plus d'un jour.
8 | * @returns null
9 | */
10 | if (!Storage.prototype.refreshIfNeeded) {
11 | Storage.prototype.refreshIfNeeded = function() {
12 | var
13 | DAY = 86400000,
14 | lastRefresh = this.getLastRefresh(),
15 | refreshTime = DAY
16 | ;
17 |
18 | if (!lastRefresh || +lastRefresh + refreshTime < Date.now()) {
19 | this.refresh();
20 | }
21 | };
22 | }
23 |
24 | if (!Storage.prototype.getLastRefresh) {
25 | Storage.prototype.getLastRefresh = function() {
26 | return this.getItem(LAST_REFRESH);
27 | };
28 | }
29 |
30 | if (!Storage.prototype.setLastRefresh) {
31 | Storage.prototype.setLastRefresh = function() {
32 | return this.setItemOrClear(LAST_REFRESH, Date.now());
33 | };
34 | }
35 |
36 | if (!Storage.prototype.refresh) {
37 | Storage.prototype.refresh = function() {
38 | this.clear();
39 | this.setLastRefresh();
40 | };
41 | }
42 |
43 |
44 | if (!Storage.prototype.setItemOrCLear) {
45 | Storage.prototype.setItemOrClear = function(keyName, keyValue) {
46 | try {
47 | this.setItem(keyName, keyValue);
48 | } catch (e) {
49 | if (e instanceof DOMException && e.name === 'QuotaExceededError') {
50 | this.refresh();
51 | } else {
52 | throw e;
53 | }
54 | }
55 | };
56 | }
57 |
58 | /**
59 | * Sature le storage. Permet essentiellement de faire des tests.
60 | */
61 | if (!Storage.prototype.saturate) {
62 | Storage.prototype.saturate = function() {
63 | var i = 1;
64 | while (i < 7500) {
65 | try {
66 | localStorage.setItem(i,
67 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mollis neque felis, in efficitur tortor vestibulum id. Sed vitae lectus volutpat, vehicula quam id, condimentum lorem. Maecenas nec mauris eu risus posuere ultricies. Integer in ultrices sem. In tincidunt bibendum maximus. Proin consectetur elit orci, maximus suscipit mi finibus eu. Morbi aliquet urna eu diam mollis elementum. Maecenas tempor ultricies elit ac lacinia. Suspendisse pharetra eros suscipit vehicula pretium. Ut id iaculis nisi. In cursus felis ac dui malesuada malesuada. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce interdum, ante sit amet rhoncus commodo, turpis felis elementum ante, et viverra erat augue at urna. Vestibulum in tincidunt erat, vitae porta odio. Mauris commodo id diam vel vehicula.');
68 | }
69 | catch (e) {
70 | throw e;
71 | }
72 | ++i;
73 | }
74 | };
75 | }
76 |
77 | }());
78 |
--------------------------------------------------------------------------------
/src/content_scripts/tabBootstrap.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var port = chrome.runtime.connect(),
3 | page = {
4 | contentType: window.document.contentType
5 | }
6 | ;
7 |
8 | port.postMessage(page);
9 |
--------------------------------------------------------------------------------
/src/icons/icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/istex-archives/istex-browser-extension/bee0055317571366997294c4e36aeb5cc05187da/src/icons/icon-48.png
--------------------------------------------------------------------------------
/src/icons/istex-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/istex-archives/istex-browser-extension/bee0055317571366997294c4e36aeb5cc05187da/src/icons/istex-128.png
--------------------------------------------------------------------------------
/src/icons/istex-128.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/istex-archives/istex-browser-extension/bee0055317571366997294c4e36aeb5cc05187da/src/icons/istex-128.xcf
--------------------------------------------------------------------------------
/src/icons/istex-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/istex-archives/istex-browser-extension/bee0055317571366997294c4e36aeb5cc05187da/src/icons/istex-16.png
--------------------------------------------------------------------------------
/src/icons/istex-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/istex-archives/istex-browser-extension/bee0055317571366997294c4e36aeb5cc05187da/src/icons/istex-48.png
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "applications": {
3 | "gecko": {
4 | "id": "istexwebextensionforreal@inist.fr",
5 | "strict_min_version": "49.0a2",
6 | "update_url": "https://addons.istex.fr/download/updates.json"
7 | }
8 | },
9 | "manifest_version": 2,
10 | "name": "ISTEX",
11 | "version": "1.3.1",
12 | "description": "An add-on for identifying ISTEX resources in the browser pages",
13 | "icons": {
14 | "16": "icons/istex-16.png",
15 | "48": "icons/istex-48.png"
16 | },
17 | "permissions": [
18 | "",
19 | "tabs",
20 | "webNavigation"
21 | ],
22 | "background": {
23 | "scripts": ["background/postInstall.js", "background/tabBootstrap.js"]
24 | },
25 | "options_ui": {
26 | "page": "options/options.html"
27 | },
28 | "content_scripts": [{
29 | "matches": [
30 | ""
31 | ],
32 | "js": [
33 | "content_scripts/tabBootstrap.js"
34 | ],
35 | "css": [
36 | "content_scripts/main.css"
37 | ]
38 | }, {
39 | "matches": [
40 | "*://scholar.google.fr/*3094930661629783031*", "*://scholar.google.com/*3094930661629783031*"
41 | ],
42 | "js": [
43 | "vendors/jquery-3.2.1.js",
44 | "content_scripts/gs-pref.js"
45 | ]
46 | }]
47 | }
48 |
--------------------------------------------------------------------------------
/src/options/options.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | font-size: 100%;
4 | background-color: #EEE;
5 | }
6 |
7 | #options {
8 | text-align: justify;
9 | border-radius: 5px;
10 | position: relative;
11 | width: 500px;
12 | margin: 0px auto;
13 | background-color: white;
14 | border: 1px solid #D2DBED;
15 | padding-left: 10px;
16 | padding-right: 10px;
17 | height: 240px;
18 | -webkit-box-shadow: #888 2px 2px 2px;
19 | }
20 |
21 | .accept {
22 | background-color: green;
23 | float: right;
24 | margin-bottom: 10px;
25 | margin-right: 5px;
26 | color: black;
27 | }
28 |
29 | .skip {
30 | background-color: red;
31 | float: right;
32 | margin-bottom: 10px;
33 | margin-right: 5px;
34 | color: black;
35 | }
--------------------------------------------------------------------------------
/src/options/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Activer la bibliothèque ISTEX pour Google Scholar ?
12 |
13 |
Si vous utilisez Google Scholar, vous pouvez installer de manière automatique la bibliothèque ISTEX. Ceci vous permettra de pouvoir consulter les documents de la base ISTEX directement dans vos résultats de recherche. L'installation ouvrira deux nouvelles fenêtres qui se fermeront automatiquement dès lors que l'installation se termine.