now able to tag words on page. Adjusted build scripts. Now able to build the chrome plugin. Updated readme.

This commit is contained in:
Nils Norman Haukås 2015-10-24 14:57:34 +02:00
parent 7a5acdaec1
commit 14e09b19da
17 changed files with 168 additions and 277 deletions

2
.gitignore vendored
View file

@ -1,3 +1,3 @@
node_modules
dist
testfolder
tmp

View file

@ -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);
});

View file

@ -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);
});

View file

@ -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.

View file

@ -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",

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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('<span id="js-selected-word">' + word + '</span>');
}
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 = '<li id="SENSEID" class="list-unstyled"><strong>WORD.</strong> EXPLANATION</li>'
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 = '<ul id="senses">' +
htmlList.join('') + '</ul>';
$('#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('<div class="tagit-body" />');
$('.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;
});

View file

@ -0,0 +1,4 @@
// used for local web testing
@@include('content_script_include.js')

View file

@ -8,7 +8,7 @@
<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<script src="vendor/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.0/angular.min.js"></script>
<script src="content_script.js"></script>
<script src="content_script_web.js"></script>
</head>
<body class="container">
@ -24,7 +24,7 @@
<h1>Sample text</h1>
<p>
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 <span id="sense-id" title="longer definition" class="tagit-tag">Cleopatra</span>, 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.
</p>
<p>
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.

View file

@ -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
}
}

View file

@ -14,7 +14,7 @@ module tagIt {
export function init (callback: () => void) {
var $ = jQuery;
$.get('menu.tpl.html', function (htmlData) {
$('body').children().wrapAll('<div class="tagit-body" />');
$('body').children().wrapAll('<div id="tagit-body" class="tagit-body" />');
$('.tagit-body').before(htmlData);
angular.bootstrap(document.getElementById("tagit-menu"), ['tagit']);
console.log('TagIt menu loaded');

View file

@ -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(<synsetJson> synsets);
this.senses = this.dataService.processSynsets(<ISynset> synsets);
});
}

View file

@ -3,7 +3,7 @@
<br/>
<button ng-click="vm.remove()" class="btn btn-default">
<button ng-click="vm.removeMenu()" class="btn btn-default">
Done tagging
</button>
@ -18,7 +18,7 @@
<input type="text" ng-model="wordToFilterBy" class="form-control" placeholder="Filter tags" />
<ul id="senses">
<li id="sense.senseid" ng-repeat="sense in vm.senses" class="list-unstyled">
<li ng-click="vm.onTagSelect(sense)" id="sense.senseid" ng-repeat="sense in vm.senses | filter:wordToFilterBy" class="list-unstyled">
<strong>{{sense.word}}</strong> {{sense.explanation}}
</li>
</ul>

View file

@ -1,3 +1,5 @@
/// <reference path="../index.interfaces.ts" />
'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);
}
}
}
}

View file

@ -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;
}

View file

@ -30,3 +30,8 @@
cursor: pointer;
}
.tagit-tag {
background-color: #EDDE45;
padding: 3px;
}