From 14e09b19da58eba99824212fd4e1a27482b38de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Norman=20Hauk=C3=A5s?= Date: Sat, 24 Oct 2015 14:57:34 +0200 Subject: [PATCH] now able to tag words on page. Adjusted build scripts. Now able to build the chrome plugin. Updated readme. --- .gitignore | 2 +- Gulpfile.js | 70 +++++++++++++------- Gulpfile_old.js | 88 ------------------------- README.md | 3 +- package.json | 1 + src/content_script.js | 43 +----------- src/content_script_include.js | 43 ++++++++++++ src/content_script_old.js | 99 ---------------------------- src/content_script_web.js | 4 ++ src/index.html | 4 +- src/index.interfaces.ts | 14 +++- src/index.ts | 2 +- src/menu/menu.controller.ts | 9 ++- src/menu/menu.tpl.html | 4 +- src/services/data.service.ts | 6 +- src/services/selectedWord.service.ts | 48 ++++++++++---- src/style.css | 5 ++ 17 files changed, 168 insertions(+), 277 deletions(-) delete mode 100644 Gulpfile_old.js create mode 100644 src/content_script_include.js delete mode 100644 src/content_script_old.js create mode 100644 src/content_script_web.js diff --git a/.gitignore b/.gitignore index 7ae2336..81ad3d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ node_modules dist -testfolder \ No newline at end of file +tmp \ No newline at end of file diff --git a/Gulpfile.js b/Gulpfile.js index 5b0ad33..0209116 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -8,6 +8,7 @@ var flatten = require('gulp-flatten'); var ts = require('gulp-typescript'); var merge = require('merge2'); var concat = require('gulp-concat'); +var fileinclude = require('gulp-file-include'); var tsProject = ts.createProject('tsconfig.json', { sortOutput : true @@ -21,16 +22,42 @@ gulp.task('scripts', function() { return tsResult.js .pipe(concat('bundle.js')) .pipe(sourcemaps.write()) // Now the sourcemaps are added to the .js file - .pipe(gulp.dest('dist')); + .pipe(gulp.dest('tmp')); }); -gulp.task('dist', ['dist-node-modules'], function () { +// build project for serving up locally +gulp.task('tmp', ['scripts', 'dist-node-modules'], function () { return gulp.src([ 'src/**/*.html', 'src/**/*.css', - 'src/**/*.js' + 'src/content_script_web.js' ], {base: 'src'}) .pipe(flatten()) + .pipe(fileinclude({ + prefix: '@@', + basePath: '@file' + })) + .pipe(gulp.dest('tmp')); +}); + +// build project for loading up in Chrome +gulp.task('dist', ['tmp'], function () { + var tmp = gulp.src([ + 'tmp/**/*', + '!tmp/index.html', + '!tmp/content_script_web.js' + ], {base: 'tmp'}); + var chromePluginResources = gulp.src([ + 'src/content_script.js', + 'manifest.json' + ]) + .pipe(flatten()) + .pipe(fileinclude({ + prefix: '@@', + basePath: '@file' + })); + + return merge([tmp, chromePluginResources]) .pipe(gulp.dest('dist')); }); @@ -40,29 +67,28 @@ gulp.task('dist-node-modules', function () { 'node_modules/angular/**/*', 'node_modules/jquery/**/*' ], {base: 'node_modules'}) - .pipe(gulp.dest('dist/vendor')); + .pipe(gulp.dest('tmp/vendor')); }); gulp.task('clean', function () { - return del('dist'); + return del(['tmp', 'dist']); }); -gulp.task('serve', ['scripts', 'dist', 'dist-node-modules'], function () { +gulp.task('serve', ['tmp'], function () { + // Serve files from the root of this project + browserSync.init({ + server: { + baseDir: "./tmp", + } + }); - // Serve files from the root of this project - browserSync.init({ - server: { - baseDir: "./dist", - } - }); - - // add browserSync.reload to the tasks array to make - // all browsers reload after tasks are complete. - gulp.watch("src/**/*.ts", ['scripts']); - gulp.watch([ - "src/**/*.html", - "src/**/*.css", - "src/**/*.js" - ], ['dist']); - gulp.watch("dist/**/*").on("change", browserSync.reload); + // add browserSync.reload to the tasks array to make + // all browsers reload after tasks are complete. + gulp.watch("src/**/*.ts", ['scripts']); + gulp.watch([ + "src/**/*.html", + "src/**/*.css", + "src/**/*.js" + ], ['tmp']); + gulp.watch("tmp/**/*").on("change", browserSync.reload); }); \ No newline at end of file diff --git a/Gulpfile_old.js b/Gulpfile_old.js deleted file mode 100644 index 9300dac..0000000 --- a/Gulpfile_old.js +++ /dev/null @@ -1,88 +0,0 @@ -'use strict'; - -var watchify = require('watchify'); -var browserify = require('browserify'); -var tsify = require('tsify'); -var gulp = require('gulp'); -var source = require('vinyl-source-stream'); -var buffer = require('vinyl-buffer'); -var gutil = require('gulp-util'); -var sourcemaps = require('gulp-sourcemaps'); -var assign = require('lodash.assign'); -var browserSync = require('browser-sync').create(); -var del = require('del'); -var flatten = require('gulp-flatten'); - -// add custom browserify options here -var customOpts = { - entries: ['./src/index.ts'], - debug: true -}; -var opts = assign({}, watchify.args, customOpts); -var b = watchify(browserify(opts)); - -// add transformations here -// i.e. b.transform(coffeeify); - -gulp.task('typescript', bundle); // so you can run `gulp js` to build the file -b.on('update', bundle); // on any dep update, runs the bundler -b.on('log', gutil.log); // output build logs to terminal - -function bundle() { - return b - .plugin('tsify', { noImplicitAny: true }) - .bundle() - // log errors if they happen - .on('error', gutil.log.bind(gutil, 'Browserify Error')) - .pipe(source('bundle.js')) - // optional, remove if you don't need to buffer file contents - .pipe(buffer()) - // optional, remove if you dont want sourcemaps - .pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file - // Add transformation tasks to the pipeline here. - .pipe(sourcemaps.write('./')) // writes .map file - .pipe(gulp.dest('./dist')); -} - -gulp.task('dist', ['dist-node-modules'], function () { - return gulp.src([ - 'src/**/*.html', - 'src/**/*.css', - 'src/**/*.js' - ], {base: 'src'}) - .pipe(flatten()) - .pipe(gulp.dest('dist')); -}); - -gulp.task('dist-node-modules', function () { - return gulp.src([ - 'node_modules/bootstrap/**/*' - ], {base: 'node_modules'}) - .pipe(gulp.dest('dist/vendor')); -}); - -gulp.task('clean', function () { - return del('dist'); -}); - -// use default task to launch Browsersync and watch JS files -gulp.task('serve', ['typescript', 'dist'], function () { - - // Serve files from the root of this project - browserSync.init({ - server: { - baseDir: "./dist", - index: "index.html" - } - }); - - // add browserSync.reload to the tasks array to make - // all browsers reload after tasks are complete. - gulp.watch([ - "dist/bundle.js", - "src/**/*.html", - "src/**/*.js", - "src/**/*.css", - ]) - .on('change', browserSync.reload); -}); \ No newline at end of file diff --git a/README.md b/README.md index e88ac07..2d09667 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ A chrome extension for tagging words with semantic information. 1. To try it out, just [download this plugin](https://github.com/nilsnh/tag-youre-it/archive/master.zip). 2. You'll need to have installed [node.js](https://nodejs.org/en/). 3. Go inside the unzipped folder and run `npm install && npm install -g gulp`. In addition to installing development dependencies it will also install Gulp which is useful for active development. -4. Run `gulp serve` to see the active prototype. +4. Run `gulp serve` to serve up the prototype for live development. +5. Run `gulp dist` to build the chrome plugin in the `dist/` folder. **Important** A [workaround for CORS](https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?utm_source=chrome-app-launcher-info-dialog) is currently needed to communicate with the server. diff --git a/package.json b/package.json index cf3b775..f402ad4 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "bootstrap": "^3.3.5", "del": "^2.0.2", "gulp-concat": "^2.6.0", + "gulp-file-include": "^0.13.7", "gulp-flatten": "^0.2.0", "gulp-typescript": "^2.9.2", "jquery": "^2.1.4", diff --git a/src/content_script.js b/src/content_script.js index 71ad8da..e109475 100644 --- a/src/content_script.js +++ b/src/content_script.js @@ -1,43 +1,4 @@ - // Save a copy of existing angular js and jquery - // Source: http://www.mattburkedev.com/multiple-angular-versions-on-the-same-page/ - var existingWindowDotAngular = window['angular']; - // create a new window.angular and a closure variable for - // angular.js to load itself into - var angular = (window.angular = {}); +// used for chrome plugin - injectScripts(); +@@include('content_script_include.js') - function injectScripts () { - - loadScript('vendor/jquery/dist/jquery.js', loadAngular); - - function loadAngular () { - loadScript('vendor/angular/angular.js', loadPluginCode); - } - - function loadPluginCode () { - loadScript('bundle.js', function () { - tagIt.init(restoreOldAngularAndJquery); - }); - } - - function restoreOldAngularAndJquery () { - // restore old angular - if (existingWindowDotAngular) { - window.angular = existingWindowDotAngular; // restore the old angular version - } - $.noConflict(); - } - - // TODO: add "script.js" to web_accessible_resources in manifest.json - // s.src = chrome.extension.getURL('script.js'); - function loadScript (scriptName, callback) { - var s = document.createElement('script'); - s.src = scriptName; - s.onload = function() { - // this.parentNode.removeChild(this); - if (callback) callback(); - }; - (document.head||document.documentElement).appendChild(s); - } - } diff --git a/src/content_script_include.js b/src/content_script_include.js new file mode 100644 index 0000000..272f544 --- /dev/null +++ b/src/content_script_include.js @@ -0,0 +1,43 @@ +// Save a copy of existing angular js and jquery +// Source: http://www.mattburkedev.com/multiple-angular-versions-on-the-same-page/ +var existingWindowDotAngular = window['angular']; +// create a new window.angular and a closure variable for +// angular.js to load itself into +var angular = (window.angular = {}); + +injectScripts(); + +function injectScripts () { + + loadScript('vendor/jquery/dist/jquery.js', loadAngular); + + function loadAngular () { + loadScript('vendor/angular/angular.js', loadPluginCode); + } + + function loadPluginCode () { + loadScript('bundle.js', function () { + tagIt.init(restoreOldAngularAndJquery); + }); + } + + function restoreOldAngularAndJquery () { + // restore old angular + if (existingWindowDotAngular) { + window.angular = existingWindowDotAngular; // restore the old angular version + } + $.noConflict(); + } + + // TODO: add "script.js" to web_accessible_resources in manifest.json + // s.src = chrome.extension.getURL('script.js'); + function loadScript (scriptName, callback) { + var s = document.createElement('script'); + s.src = scriptName; + s.onload = function() { + // this.parentNode.removeChild(this); + if (callback) callback(); + }; + (document.head||document.documentElement).appendChild(s); + } +} diff --git a/src/content_script_old.js b/src/content_script_old.js deleted file mode 100644 index 51d0596..0000000 --- a/src/content_script_old.js +++ /dev/null @@ -1,99 +0,0 @@ -$(document).ready(function () { - - var isMenuShown = false; - var currentlySelectedWord; - - // Find currently selected word - function processSelection () { - var focused = document.activeElement; - var selectedText; - if (focused) { - try { - selectedText = focused.value.substring( - focused.selectionStart, focused.selectionEnd); - } catch (err) { - } - } - if (selectedText == undefined) { - var sel = window.getSelection(); - var selectedText = sel.toString(); - } - if (selectedText) { - currentlySelectedWord = selectedText; - displaySelectedWord(currentlySelectedWord); - getSensesFromServer(currentlySelectedWord); - } - } - - function displaySelectedWord (word) { - $('#js-selected-word') - .replaceWith('' + word + ''); - } - - function getSensesFromServer (word) { - if (!word) { - return; - }; - - var serverUrl = 'http://lexitags.dyndns.org/server/lexitags2/Semtags?data={"word":"QUERYTOREPLACE"}' - $.get(serverUrl.replace('QUERYTOREPLACE', word), function (serverResponse) { - console.log(serverResponse); - updateList(serverResponse); - }); - - function updateList (serverResponse) { - var senses = serverResponse.senses; - senses.reverse(); - var listTemplate = '
  • WORD. EXPLANATION
  • ' - var htmlList = []; - for (var i = senses.length - 1; i >= 0; i--) { - htmlList.push(listTemplate - .replace('SENSEID', senses[i].senseid) - .replace('WORD', senses[i].word) - .replace('EXPLANATION', senses[i].explanation)) - }; - - htmlList = ''; - - $('#senses').replaceWith(htmlList); - - } - - } - - document.addEventListener('click', function(evt) { - if (!document.hasFocus()) { - return true; - } - processSelection(); - // evt.stopPropagation(); - // evt.preventDefault(); - }, false); - - /* - Take the existing content, make it narrower and - insert a menu for tagging up content. - */ - function addMenu () { - if (isMenuShown) return; - $.get('example1.menu.html', function (htmlData) { - $('body').children().wrapAll('
    '); - $('.tagit-body').before(htmlData); - $('#js-hide-menu').click(removeMenu); - isMenuShown = true; - }); - } - function removeMenu () { - if (!isMenuShown) return; - $('.tagit-body').children().unwrap(); - $('.tagit-menu').remove(); - isMenuShown = false; - } - - // Beware order is significant - $('#js-show-menu').click(addMenu); - // addMenu(); - // isMenuShown = true; - -}); \ No newline at end of file diff --git a/src/content_script_web.js b/src/content_script_web.js new file mode 100644 index 0000000..6e119ce --- /dev/null +++ b/src/content_script_web.js @@ -0,0 +1,4 @@ +// used for local web testing + +@@include('content_script_include.js') + diff --git a/src/index.html b/src/index.html index e704875..fdbe58e 100644 --- a/src/index.html +++ b/src/index.html @@ -8,7 +8,7 @@ - + @@ -24,7 +24,7 @@

    Sample text

    - The Triumph of Cleopatra, also known as Cleopatra's Arrival in Cilicia[1] and The Arrival of Cleopatra in Cilicia,[2] is an oil painting by English artist William Etty. It was first exhibited in 1821, and is now in the Lady Lever Art Gallery in Port Sunlight across the River Mersey from Liverpool. During the 1810s Etty had become widely respected among staff and students at the Royal Academy of Arts, in particular for his use of colour and ability to paint realistic flesh tones. Despite having exhibited at every Summer Exhibition since 1811 he attracted little commercial or critical interest. In 1820 he exhibited The Coral Finder, which showed nude figures on a gilded boat. This painting attracted the attention of Sir Francis Freeling, who commissioned a similar painting on a more ambitious scale. + The Triumph of Cleopatra, also known as Cleopatra's Arrival in Cilicia[1] and The Arrival of Cleopatra in Cilicia,[2] is an oil painting by English artist William Etty. It was first exhibited in 1821, and is now in the Lady Lever Art Gallery in Port Sunlight across the River Mersey from Liverpool. During the 1810s Etty had become widely respected among staff and students at the Royal Academy of Arts, in particular for his use of colour and ability to paint realistic flesh tones. Despite having exhibited at every Summer Exhibition since 1811 he attracted little commercial or critical interest. In 1820 he exhibited The Coral Finder, which showed nude figures on a gilded boat. This painting attracted the attention of Sir Francis Freeling, who commissioned a similar painting on a more ambitious scale.

    Rome's history spans more than two and a half thousand years. While Roman mythology dates the founding of Rome at only around 753 BC, the site has been inhabited for much longer, making it one of the oldest continuously occupied sites in Europe.[5] The city's early population originated from a mix of Latins, Etruscans and Sabines. Eventually, the city successively became the capital of the Roman Kingdom, the Roman Republic and the Roman Empire, and is regarded as one of the birthplaces of Western civilization and as the first ever metropolis.[6] It is referred to as "Roma Aeterna" (The Eternal City) [7] and "Caput Mundi" (Capital of the World), two central notions in ancient Roman culture. diff --git a/src/index.interfaces.ts b/src/index.interfaces.ts index c76a300..c97a561 100644 --- a/src/index.interfaces.ts +++ b/src/index.interfaces.ts @@ -2,14 +2,14 @@ module tagIt { - export interface synsetJson { + export interface ISynset { config: Object, data: { senses: string[] } } - export interface tagItAngularScope extends angular.IScope { + export interface IVMScope extends angular.IScope { vm : Object; } @@ -22,5 +22,15 @@ module tagIt { word: string } + /* + sentence: represents everything contained by punctuations. + context: represents ten chars "in both directions" from the word. + */ + export interface ISenseTag { + userEmail: string, + senseid: string, + sentence: string, + context: string + } } diff --git a/src/index.ts b/src/index.ts index a60e2b6..5118245 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,7 @@ module tagIt { export function init (callback: () => void) { var $ = jQuery; $.get('menu.tpl.html', function (htmlData) { - $('body').children().wrapAll('

    '); + $('body').children().wrapAll('
    '); $('.tagit-body').before(htmlData); angular.bootstrap(document.getElementById("tagit-menu"), ['tagit']); console.log('TagIt menu loaded'); diff --git a/src/menu/menu.controller.ts b/src/menu/menu.controller.ts index 09e951b..e4c9eda 100644 --- a/src/menu/menu.controller.ts +++ b/src/menu/menu.controller.ts @@ -19,7 +19,7 @@ module tagIt { "SelectedWordService" ]; - constructor ($scope: tagItAngularScope, $log: angular.ILogService, + constructor ($scope: IVMScope, $log: angular.ILogService, DataService: DataService, SelectedWordService: SelectedWordService) { $scope.vm = this; @@ -27,17 +27,22 @@ module tagIt { this.$scope = $scope; this.dataService = DataService; this.selectedWordService = SelectedWordService; + // Wire up clicklistener this.selectedWordService.wireUpListener(this.onWordSelected, this.onWordDeSelected); } + onTagSelect (sense: ISense) { + this.selectedWordService.addTagToPage(sense); + } + onWordSelected = (newWord : string) => { this.selectedWord = newWord; this.dataService.callServer(newWord) .then((synsets : Object) => { this.$log.debug(synsets); - this.senses = this.dataService.processSynsets( synsets); + this.senses = this.dataService.processSynsets( synsets); }); } diff --git a/src/menu/menu.tpl.html b/src/menu/menu.tpl.html index 8f6ccce..3271558 100644 --- a/src/menu/menu.tpl.html +++ b/src/menu/menu.tpl.html @@ -3,7 +3,7 @@
    - @@ -18,7 +18,7 @@
      -
    • +
    • {{sense.word}} {{sense.explanation}}
    diff --git a/src/services/data.service.ts b/src/services/data.service.ts index 353745f..6b889f7 100644 --- a/src/services/data.service.ts +++ b/src/services/data.service.ts @@ -1,3 +1,5 @@ +/// + 'use strict'; module tagIt { @@ -19,7 +21,7 @@ module tagIt { return this.$http.get(this.serverUrl + this.createQuery(word)); } - processSynsets (synsets: synsetJson) : string[] { + processSynsets (synsets: ISynset) : string[] { return synsets.data.senses; } @@ -34,4 +36,4 @@ module tagIt { .replace(/QUERYTOREPLACE/, word); } } -} +} \ No newline at end of file diff --git a/src/services/selectedWord.service.ts b/src/services/selectedWord.service.ts index 7029c5f..1ed1ff9 100644 --- a/src/services/selectedWord.service.ts +++ b/src/services/selectedWord.service.ts @@ -8,6 +8,7 @@ module tagIt { export class SelectedWordService { $log : ng.ILogService; + currentSelectionRange : any; static $inject = ["$log"]; constructor($log: ng.ILogService) { @@ -17,13 +18,15 @@ module tagIt { wireUpListener (callbackOnSelectFunc : (result : Object) => void, callbackOnDeSelectFunc : () => void) { var that = this; - document.addEventListener('click', (evt : any) => { + document.getElementById('tagit-body') + .addEventListener('click', (evt : any) => { if (!document.hasFocus()) { return true; } - var selectedWord = that.findSelection(); - if(selectedWord) { - callbackOnSelectFunc(joinLongWords(selectedWord)); + var selection = that.findSelection(); + if(selection) { + this.currentSelectionRange = selection.getRangeAt(0).cloneRange(); + callbackOnSelectFunc(joinLongWords(selection.toString())); } else { callbackOnDeSelectFunc(); } @@ -36,23 +39,40 @@ module tagIt { } } + // place spans around a tagged word. + addTagToPage (sense : ISense) { + var windowSelection = window.getSelection(); + var range = this.currentSelectionRange; + var span : HTMLSpanElement = document.createElement('span'); + span.id = sense.senseid; + span.title = sense.explanation; + span.className = 'tagit-tag'; + range.surroundContents(span); + windowSelection.removeAllRanges(); + // windowSelection.addRange(range); + this.$log.debug('addTagToPage'); + } + private findSelection () { var focused : any = document.activeElement; var selectedText : string; - if (focused) { - try { - selectedText = focused.value.substring( - focused.selectionStart, focused.selectionEnd); - } catch (err) { - } - } + // try grabbing text from an input or textarea field + // commenting this until we need to figure out tagging of editable fields + // if (focused) { + // try { + // selectedText = focused.value.substring( + // focused.selectionStart, focused.selectionEnd); + // } catch (err) { + // } + // } + // if previous method did not work ask window for selection if (selectedText == undefined) { - var sel = window.getSelection(); - var selectedText = sel.toString(); + var currentSelection : Selection = window.getSelection(); + var selectedText = currentSelection.toString(); } if (selectedText) { this.$log.debug('text that was selected: ' + selectedText); - return selectedText; + return currentSelection; } else { return; } diff --git a/src/style.css b/src/style.css index c1b9b78..322d713 100644 --- a/src/style.css +++ b/src/style.css @@ -30,3 +30,8 @@ cursor: pointer; } +.tagit-tag { + background-color: #EDDE45; + padding: 3px; +} +