├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .prettierrc.json
├── .travis.yml
├── LICENSE
├── README.md
├── app
├── app-controller.js
├── app-test.js
├── app.js
├── components
│ ├── grid
│ │ ├── grid-controller.js
│ │ ├── grid-test.js
│ │ ├── grid.html
│ │ └── grid.js
│ ├── header
│ │ ├── header-controller.js
│ │ ├── header-test.js
│ │ ├── header.html
│ │ └── header.js
│ ├── keyboard
│ │ ├── keyboard-controller.js
│ │ ├── keyboard-test.js
│ │ ├── keyboard.html
│ │ └── keyboard.js
│ ├── phrase
│ │ ├── phrase-controller.js
│ │ ├── phrase-test.js
│ │ ├── phrase.html
│ │ └── phrase.js
│ ├── recent
│ │ ├── recent-controller.js
│ │ ├── recent-test.js
│ │ ├── recent.html
│ │ └── recent.js
│ └── symbol
│ │ ├── symbol-controller.js
│ │ ├── symbol-test.js
│ │ ├── symbol.html
│ │ └── symbol.js
├── css
│ ├── font.css
│ ├── icon.css
│ ├── norm.css
│ ├── sortable.css
│ └── style.css
├── data
│ ├── en.json
│ ├── test-en.json
│ └── tr.json
├── img
│ ├── conj-en-noun
│ │ ├── from.svg
│ │ ├── in.svg
│ │ ├── my.svg
│ │ ├── simple.svg
│ │ ├── that.svg
│ │ ├── the.svg
│ │ ├── this.svg
│ │ ├── to.svg
│ │ └── your.svg
│ ├── conj-en
│ │ ├── ableMode.svg
│ │ ├── certFutuTence.svg
│ │ ├── imperMode.svg
│ │ ├── ogrGecZam.svg
│ │ ├── possibMode.svg
│ │ ├── presContTence.svg
│ │ ├── simFutuTence.svg
│ │ ├── simPastTence.svg
│ │ ├── simPresTence.svg
│ │ └── subjunMode.svg
│ ├── conj-tr-noun
│ │ ├── benim.svg
│ │ ├── bu.svg
│ │ ├── de.svg
│ │ ├── den.svg
│ │ ├── e.svg
│ │ ├── i.svg
│ │ ├── senin.svg
│ │ ├── simple.svg
│ │ ├── su.svg
│ │ ├── şu.svg
│ │ ├── şu.svg
│ │ └── şşşşu.svg
│ └── conj-tr
│ │ ├── dilKip.svg
│ │ ├── emrKip.svg
│ │ ├── gelZam.svg
│ │ ├── genZam.svg
│ │ ├── gerKip.svg
│ │ ├── gorGecZam.svg
│ │ ├── istKip.svg
│ │ ├── ogrGecZam.svg
│ │ └── simZam.svg
├── index.html
├── js
│ ├── constants.js
│ ├── init.js
│ ├── utils-test.js
│ └── utils.js
├── mocks.js
├── otsimo.json
├── services
│ ├── conjunction.js
│ ├── event.js
│ ├── global.js
│ ├── localstorage-test.js
│ ├── localstorage.js
│ ├── otsimo-handler.js
│ └── tts.js
├── symbols
│ ├── aac-test-en
│ │ ├── data.json
│ │ ├── images
│ │ │ ├── i.svg
│ │ │ ├── it.svg
│ │ │ ├── they.svg
│ │ │ ├── we.svg
│ │ │ └── you.svg
│ │ ├── main.js
│ │ └── metadata.json
│ ├── back.svg
│ ├── next.svg
│ └── prev.svg
└── woff
│ ├── font-latin-ext.woff
│ ├── font.woff
│ └── icon.woff
├── ci
├── Dockerfile
└── start.sh
├── e2e-tests
├── protractor.conf.js
└── scenarios.js
├── generator.js
├── i18n
├── en
│ ├── ss1_en_1024x768.png
│ ├── ss2_en_1024x768.png
│ └── ss3_en_1024x768.png
├── icon.png
├── logo.png
└── tr
│ ├── ss1_tr_1024x768.png
│ ├── ss2_tr_1024x768.png
│ └── ss3_tr_1024x768.png
├── jsconfig.json
├── karma.conf.js
├── karma.shim.js
├── package.json
├── tasks
├── update-symbols.js
└── version.js
├── webpack.config.js
├── webpack.dev.config.js
├── webpack.prod.config.js
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
1 | app/**/*-test.js
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true
5 | },
6 | "globals": {
7 | "otsimo": true,
8 | "angular": true,
9 | "responsiveVoice": true
10 | },
11 | "extends": "airbnb",
12 | "rules": {
13 | "func-names": "off",
14 | "no-console": "off",
15 | "class-methods-use-this": "off"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | dist
27 | spec
28 | .vscode
29 | app/symbols/aac-en
30 | app/symbols/aac-tr
31 | out
32 | public
33 |
34 | # Dependency directories
35 | node_modules
36 | jspm_packages
37 |
38 | # Optional npm cache directory
39 | .npm
40 |
41 | # Optional REPL history
42 | .node_repl_history
43 | typings
44 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all"
3 | }
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '6.1'
4 | before_install:
5 | - export CHROME_BIN=chromium-browser
6 | - export DISPLAY=:99.0
7 | - sh -e /etc/init.d/xvfb start
8 | install:
9 | - npm install eslint
10 | - npm install
11 | script:
12 | - npm run test-travis
13 | notifications:
14 | email: false
15 | slack:
16 | secure: CXpzdWAJUREDEfTSmtCE3sq0eVvJc4Q0Ci57yu6670HYi/Nowx9CXQlv7u8TwUakL22NygGB8POGHtCJZ1CBDV44T1T81xHnsJYBe2AkbML9KhTZwP4v4STtgz7wLc9KJ0LtMD/F3EpgfWW4xj7eth8mQWj8V4hMxLTQArhvMH1LtdEF5o102ZtFEfNKlged/jSXl/NOL47ONl/GM13ZE9riocpaAKztlG/ZxAgAd5gGk08Gv7GkYug3NWi2nSPDrYqnOkORBCjr3sXzj2djDCYoKjdBIXe9gLnX3iCC+QnN0NbxHcz2jHsyKSohCWrbjzHgujrNoY7l/HyJNUI2edgIZSzmST1zuV6r/jxu88pictjBHcy7pjYgyS+ob+V2AnEr8/n6mHiqQL3dFIkmdO4UDq+oK1NJkJC54xp8HhVtMafuuDh4BsY6pBtYiuD65xHwmvJSm35KDY8fYFEZHq/zepio+cLKfpOUrHYfzXzEkTFD5d4zBMKyvzQRwQdVuLoE7GUNYW0tkPgJ3onA0aQI/Ih2c11hh/kajjZQeQKSVJevBSmn8IIIBSSD5VRHyLbf9/Qp6OtFs5RNKHae6dl0K2iKCzHIrfAp1vo4eJwkJWVvwrfSDGjU3E53IhbOLagaKbNj/iHNQR/J/4YkOsDdyabZwK4tHn4+KUbdr1w=
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Otsimo AAC PECS App
2 |
3 | An app that helps kids with autism to compose phrases.
4 |
5 | Otsimo AAC is an application that ables kids with autism who can not speak to compose phrases by tapping on the words. Otsimo AAC uses picture exchange communication system.
6 |
7 | This project was tested with the latest version of nodeJS and npm, please make sure you have atleast node.js 5+ and NPM 3+ installed.
8 |
9 | ## Usage & Develop
10 |
11 | - Clone or fork this repository
12 | - run `npm install` to install dependencies
13 | - run `npm run start` to fire up dev server
14 | - open browser to [`http://localhost:3001`](http://localhost:3001)
15 |
16 | ## Build
17 |
18 | to create a ready production distribution package of the project please run:
19 |
20 | ```
21 | npm run build
22 | ```
23 |
24 | after running build the generated files will be available at `/dist`
25 |
26 | ## Testing
27 |
28 | This seed is has protractor and karma for end to end testing and unit testing respectively.
29 |
30 | ### Unit Testing
31 |
32 | make sure your tests are named with a `-test.js` suffix then. to run karma simply run:
33 |
34 | ```
35 | npm test
36 | ```
37 |
38 | ### End to end Testing
39 |
40 | to start protractor tests please run:
41 |
42 | ```
43 | npm run protractor
44 | ```
45 |
46 | ### Update Symbols
47 |
48 | to update the symbols:
49 |
50 | ```
51 | npm run update-symbols
52 | ```
53 |
--------------------------------------------------------------------------------
/app/app-test.js:
--------------------------------------------------------------------------------
1 | /* eslint-env jasmine */
2 | import {module, inject} from 'mocks';
3 | import ngTouch from 'angular-touch';
4 | import FastClick from 'fastclick';
5 | import AppController from './app-controller';
6 | import app from './app';
7 | //import * as CONSTANT from './js/constants';
8 | import Global from 'services/global';
9 | import EventManager from 'services/event';
10 | import TTSManager from 'services/tts';
11 | import LSManager from 'services/localstorage';
12 | //import MOCKED_CONSTANT from './js/constants';
13 |
14 |
15 | Array.prototype.unique = function() {
16 | var u = {},
17 | a = [];
18 | for (var i = 0, l = this.length; i < l; ++i) {
19 | if (u.hasOwnProperty(this[i])) {
20 | continue;
21 | }
22 | a.push(this[i]);
23 | u[this[i]] = 1;
24 | }
25 | return a;
26 | }
27 |
28 | function randomString(len, charSet) {
29 | charSet = charSet || 'abcdefghijklmnopqrstuvwxyz';
30 | let randomString = '';
31 | for (let i = 0; i < len; i++) {
32 | let randomPoz = Math.floor(Math.random() * charSet.length);
33 | randomString += charSet.substring(randomPoz, randomPoz + 1);
34 | }
35 | return randomString;
36 | }
37 |
38 | function generateSymbol(length, normal, derivable, group) {
39 | let classes = [];
40 | if (normal) {
41 | classes.push("main");
42 | }
43 | if (derivable) {
44 | classes.push("derivable");
45 | }
46 | if (group) {
47 | classes.push("group");
48 | }
49 | let i = 0;
50 | let symbArray = [];
51 | while (i < length) {
52 | let randString = randomString(7);
53 | let wordObj = {
54 | title: randString,
55 | slug: randString,
56 | type: 'noun',
57 | parent: 'main'
58 | };
59 | wordObj.class = classes[Math.floor(Math.random() * (classes.length - 1)) + 1];
60 | symbArray.push(wordObj);
61 | i++;
62 | }
63 | return symbArray;
64 | }
65 |
66 | Array.prototype.contains = function(obj) {
67 | return this.indexOf(obj) > -1;
68 | };
69 |
70 | String.prototype.contains = function(it) {
71 | return this.indexOf(it) != -1;
72 | };
73 |
74 | describe('aacApp.main module', () => {
75 | beforeEach(module("aacApp"));
76 |
77 | let $controller,
78 | $timeout;
79 | let appCtrl;
80 | let g,
81 | event,
82 | tts,
83 | ls,
84 | ots;
85 | beforeEach(inject((_$controller_, _$timeout_) => {
86 | // The injector unwraps the underscores (_) from around the parameter names when matching
87 | $controller = _$controller_;
88 | $timeout = _$timeout_;
89 | }));
90 |
91 | beforeEach(() => {
92 | g = new Global();
93 | g.pushToCurrentPhrase = function(obj) {
94 | keyboardCtrl.$scope.global.currentPhrase.push(obj);
95 | }
96 | g.extendedArray = [];
97 | g.mainArray = [];
98 | event = {
99 | appPhrase: function() {},
100 | appWord: function() {}
101 | };
102 | tts = {
103 | speak: function() {}
104 | };
105 | ls = new LSManager();
106 | g.changeTab = function() {}
107 | //$controller(dependentController, {$scope:{},$global:g});
108 | ots = {
109 | init: function() {
110 | return {run: function() {}, onResolutionChanged: function() {}, kv: {}, child: {}}
111 | }
112 | };
113 | });
114 |
115 | describe('app controller', () => {
116 | it('should be defined', () => {
117 | appCtrl = $controller(AppController, {
118 | $scope: {},
119 | $global: g,
120 | $timeout: $timeout,
121 | EventManager: event,
122 | TTSManager: tts,
123 | OtsimoHandler: ots
124 | });
125 | expect(appCtrl).toBeDefined();
126 | });
127 | });
128 |
129 | describe('setUIText', () => {
130 | it('should set texts and variables from otsimo to scope', () => {
131 | appCtrl = $controller(AppController, {
132 | $scope: {},
133 | $global: g,
134 | $timeout: $timeout,
135 | EventManager: event,
136 | TTSManager: tts,
137 | OtsimoHandler: ots
138 | });
139 | appCtrl.otsimo.kv.pageText1 = "pagetextmock";
140 | appCtrl.setUIText();
141 | expect(appCtrl.$scope.pageText1).toBe("pagetextmock");
142 | });
143 | });
144 |
145 | describe('language', () => {
146 | it('should be set!', () => {
147 | appCtrl = $controller(AppController, {
148 | $scope: {},
149 | $global: g,
150 | $timeout: $timeout,
151 | EventManager: event,
152 | TTSManager: tts,
153 | OtsimoHandler: ots
154 | });
155 | appCtrl.otsimo.child.language = "tr";
156 | appCtrl.setSettings();
157 | expect(appCtrl.$scope.global.language).toBe("tr");
158 | appCtrl.otsimo.child.language = "en";
159 | appCtrl.setSettings();
160 | expect(appCtrl.$scope.global.language).toBe("en");
161 | });
162 | });
163 |
164 | describe('map', () => {
165 | it('should be expected', () => {
166 | appCtrl = $controller(AppController, {
167 | $scope: {},
168 | $global: g,
169 | $timeout: $timeout,
170 | EventManager: event,
171 | TTSManager: tts,
172 | OtsimoHandler: ots
173 | });
174 | appCtrl.otsimo.child.language = "tr";
175 |
176 | appCtrl.$scope.global.extendedArray = [];
177 | appCtrl.$scope.global.extendedTitleArray = [];
178 | appCtrl.$scope.global.extendedSlugArray = [];
179 |
180 | appCtrl.$scope.global.mainArray = [
181 | {
182 | title: "aasd",
183 | slug: "aasd",
184 | synonym: ["sdsdsd"]
185 | }, {
186 | title: "asd qwe",
187 | slug: "asd-qwe",
188 | synonym: ["qwe123"]
189 | }
190 | ];
191 | appCtrl.initExtendedSymbols();
192 | expect(appCtrl.$scope.global.extendedArray).toBeDefined();
193 | expect(appCtrl.$scope.global.extendedTitleArray.length).toBe(4);
194 | expect(appCtrl.$scope.global.extendedSlugArray.length).toBe(4);
195 |
196 | appCtrl.$scope.global.extendedArray = [];
197 | appCtrl.$scope.global.extendedTitleArray = [];
198 | appCtrl.$scope.global.extendedSlugArray = [];
199 |
200 | appCtrl.$scope.global.mainArray = [
201 | {
202 | title: "aasd",
203 | slug: "aasd",
204 | synonym: ["sdsdsd"],
205 | type: "noun"
206 | }, {
207 | title: "asd qwe",
208 | slug: "asd-qwe",
209 | synonym: ["qwe123"],
210 | type: "noun"
211 | }
212 | ];
213 | appCtrl.initExtendedSymbols();
214 | expect(appCtrl.$scope.global.extendedArray).toBeDefined();
215 | expect(appCtrl.$scope.global.extendedTitleArray.length).toBe(22);
216 | expect(appCtrl.$scope.global.extendedSlugArray.length).toBe(22);
217 |
218 | appCtrl.$scope.global.extendedArray = [];
219 | appCtrl.$scope.global.extendedTitleArray = [];
220 | appCtrl.$scope.global.extendedSlugArray = [];
221 |
222 | appCtrl.$scope.global.mainArray = [
223 | {
224 | title: "aasd",
225 | slug: "aasd",
226 | synonym: [],
227 | type: "verb"
228 | }, {
229 | title: "asd qwe",
230 | slug: "asd-qwe",
231 | synonym: [],
232 | type: "verb"
233 | }
234 | ];
235 | appCtrl.initExtendedSymbols();
236 | expect(appCtrl.$scope.global.extendedArray.length).toBe(128);
237 | expect(appCtrl.$scope.global.extendedTitleArray.length).toBe(128);
238 | expect(appCtrl.$scope.global.extendedSlugArray.length).toBe(128);
239 | });
240 | });
241 |
242 | });
243 |
--------------------------------------------------------------------------------
/app/app.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular'
2 | import ngsortable from 'ng-sortable'
3 | import otsimo from 'otsimo';
4 | import headerModule from 'components/header/header'
5 | import phraseModule from 'components/phrase/phrase'
6 | import gridModule from 'components/grid/grid'
7 |
8 | import AppController from './app-controller'
9 | import Global from 'services/global'
10 | import EventManager from 'services/event'
11 | import TTSManager from 'services/tts'
12 | import LSManager from 'services/localstorage'
13 | import ConjunctionManager from 'services/conjunction'
14 | import OtsimoHandler from 'services/otsimo-handler';
15 |
16 | let aacApp = angular.module('aacApp', [headerModule.name, phraseModule.name, gridModule.name, 'ngTouch', 'as.sortable']);
17 |
18 | aacApp.factory('$global', () => new Global());
19 | aacApp.factory('EventManager', () => new EventManager());
20 | aacApp.factory('TTSManager', () => new TTSManager());
21 | aacApp.factory('LSManager', () => new LSManager());
22 | aacApp.factory('ConjunctionManager', () => new ConjunctionManager());
23 | aacApp.factory('OtsimoHandler', () => new OtsimoHandler());
24 | aacApp.controller('aac-controller', AppController);
25 |
26 | // Unhandled Rejection -> false
27 | // Check this on other releases ^1.6.0
28 | aacApp.config([
29 | '$qProvider',
30 | function ($qProvider) {
31 | $qProvider.errorOnUnhandledRejections(false);
32 | }
33 | ]);
34 | // Bootstrap the angular when document is ready.
35 |
36 | otsimo.run(() => {
37 | angular.element(document).ready(() => {
38 | angular.bootstrap(document.body, [aacApp.name]);
39 | });
40 | });
41 |
42 | otsimo.init({});
43 |
--------------------------------------------------------------------------------
/app/components/grid/grid-controller.js:
--------------------------------------------------------------------------------
1 | import * as CONSTANT from '../../js/constants';
2 |
3 | /**
4 | *
5 | * @class GridController
6 | */
7 | export default class GridController {
8 | /**
9 | * Creates an instance of GridController.
10 | *
11 | * @param {any} $scope
12 | * @param {any} $global
13 | * @param {any} $timeout
14 | * @param {TTSManager} TTSManager
15 | * @param {ConjunctionManager} ConjunctionManager
16 | *
17 | */
18 | constructor($scope, $global, $timeout, TTSManager, ConjunctionManager) {
19 | this.$scope = $scope;
20 | this.$scope.global = $global;
21 | this.global = $global;
22 | this.$timeout = $timeout;
23 | this.tts = TTSManager;
24 | this.conj = ConjunctionManager;
25 |
26 | // Initilize variables for controller.
27 | this.$scope.pageNo = 0;
28 | this.$scope.maxPageNo = 0;
29 | this.prevPageNo = 0;
30 | this.prevGroupStack = [];
31 | this.prevDerivableStack = [];
32 |
33 | // Call controllerInit
34 | this.controllerInit();
35 | }
36 |
37 | /**
38 | * Create or facilitate new functions for $scope or $global service.
39 | */
40 | controllerInit() {
41 | this.global.go2FirstPage = this.go2FirstPage.bind(this);
42 | this.global.getPage = this.getPage.bind(this);
43 | this.global.changeTab = this.changeTab.bind(this);
44 | this.global.pushToCurrentPhrase = this.pushToCurrentPhrase.bind(this);
45 | }
46 |
47 | /**
48 | * Handles logical actions about tab change.
49 | *
50 | * @param {string} tabExp - the ne tab
51 | */
52 | changeTab(tabExp) {
53 | let mArray = this.global.mainArray;
54 | switch (tabExp) {
55 | case CONSTANT.TAB_MAIN:
56 | this.$scope.mainDataUnpaged = mArray.filter((f) => f.parent === CONSTANT.CLASS_MAIN);
57 | break;
58 | case CONSTANT.TAB_DERIVABLE:
59 | this.prevPageNo = this.$scope.pageNo;
60 | this.pushNavigationHistory(CONSTANT.TAB_DERIVABLE);
61 | this.$scope.pageNo = 0;
62 | this.$scope.mainDataUnpaged = mArray.filter(f => f.parent === this.global.currentDerivable);
63 | break;
64 | case CONSTANT.TAB_GROUP:
65 | this.prevPageNo = this.$scope.pageNo;
66 | this.pushNavigationHistory(CONSTANT.TAB_GROUP);
67 | this.$scope.pageNo = 0;
68 | this.$scope.mainDataUnpaged = mArray.filter(f => f.parent === this.global.currentGroup);
69 | break;
70 | }
71 | this.updateGridSlicing();
72 | }
73 | /**
74 | * Stores the history of navigation (navigation history used for 'goback' navigation)
75 | *
76 | * @param {string} tab for history pushing
77 | */
78 | pushNavigationHistory(tab) {
79 | switch (tab) {
80 | case CONSTANT.TAB_DERIVABLE:
81 | let currentD = this.global.currentDerivable;
82 | if (this.prevDerivableStack[this.prevDerivableStack.length - 1] !== currentD) {
83 | this.prevDerivableStack.push(currentD);
84 | }
85 | break;
86 | case CONSTANT.TAB_GROUP:
87 | let currentG = this.global.currentGroup;
88 | if (this.prevGroupStack[this.prevGroupStack.length - 1] !== currentG) {
89 | this.prevGroupStack.push(currentG);
90 | }
91 | break;
92 | }
93 | }
94 |
95 | /**
96 | * Handles paging of the grid. Slices the currently showed array into grid size.
97 | *
98 | * @param {number} symbolQuantity
99 | */
100 | sliceArray(symbolQuantity) {
101 | if (this.global.isHome === 1 && this.$scope.pageNo !== 0) {
102 | this.$scope.mainData = this.$scope.mainDataUnpaged.slice(parseInt(this.$scope.pageNo * symbolQuantity + 1), parseInt((this.$scope.pageNo + 1) * symbolQuantity + 1)).map(this.mapStyle);
103 | } else {
104 | this.$scope.mainData = this.$scope.mainDataUnpaged.slice(this.$scope.pageNo * symbolQuantity, (this.$scope.pageNo + 1) * symbolQuantity).map(this.mapStyle);
105 | }
106 | this.$scope.maxPageNo = this.returnMaxPage();
107 | }
108 |
109 | /**
110 | * Gives appropriate classNames with respect to the class of the symbol
111 | * in the currently showing symbol array.
112 | *
113 | * @param {Object} symb - the symbol object
114 | * @returns {Object} the symbol object
115 | */
116 | mapStyle(symb) {
117 | if (symb.class === CONSTANT.CLASS_GROUP) {
118 | symb.style = 'gridGroup';
119 | } else {
120 | symb.style = 'gridType-' + symb.type;
121 | }
122 | return symb;
123 | }
124 |
125 | /**
126 | * Calculates the max page quantity.
127 | * in the currently showing symbol array.
128 | * @returns {number} max page
129 | */
130 | returnMaxPage() {
131 | return Math.ceil(this.$scope.mainDataUnpaged.length / this.global.gridQuantity + 0.0001) - 1;
132 | }
133 |
134 | /**
135 | * Navigation function to go to prior page or view.
136 | * Go back symbol only shows up when there is a prior view.
137 | */
138 | goBack() {
139 | this.prevGroupStack.pop();
140 | this.prevDerivableStack.pop();
141 | this.$scope.pageNo = this.prevPageNo;
142 | let prevGroup = this.prevGroupStack[this.prevGroupStack.length - 1];
143 | let prevDerivable = this.prevDerivableStack[this.prevDerivableStack.length - 1];
144 |
145 | if (this.prevGroup) {
146 | this.global.currentGroup = this.prevGroup;
147 | this.global.changeCurrentTab(CONSTANT.TAB_GROUP);
148 | } else if (this.prevDerivable) {
149 | this.global.currentDerivable = this.prevDerivable;
150 | this.global.changeCurrentTab(CONSTANT.TAB_DERIVABLE);
151 | } else {
152 | this.global.currentGroup = '';
153 | this.global.currentDerivable = '';
154 | this.global.changeCurrentTab(CONSTANT.TAB_MAIN);
155 | }
156 |
157 | this.updateGridSlicing();
158 | }
159 |
160 | /**
161 | * Navigation function to go to next page
162 | * In current symbol array.
163 | */
164 | goNextMain() {
165 | this.$scope.pageNo++;
166 | this.updateGridSlicing();
167 | }
168 | /**
169 | * Navigation function to go to previous page
170 | * In current symbol array.
171 | */
172 | goPrevMain() {
173 | if (this.$scope.pageNo !== 0) {
174 | this.$scope.pageNo--;
175 | }
176 | this.updateGridSlicing();
177 | }
178 | /**
179 | * Updates sliceAmount and calls sliceArray function
180 | * Also calls animateSlicing function.
181 | */
182 | updateGridSlicing() {
183 | let sliceAmount;
184 | if (this.global.isHome === 1 && this.$scope.pageNo === 0) {
185 | sliceAmount = this.global.gridQuantity - 1;
186 | } else {
187 | sliceAmount = this.global.gridQuantity - 2;
188 | }
189 | this.sliceArray(sliceAmount);
190 | this.animateSlicing();
191 | return sliceAmount;
192 | }
193 |
194 | /**
195 | * Navigates user to the first page
196 | * In the currently showing symbol array.
197 | * @returns {number} current pageNo for test
198 | */
199 | go2FirstPage() {
200 | let pageNo = this.$scope.pageNo = 0;
201 | this.updateGridSlicing();
202 | return pageNo;
203 | }
204 |
205 | /**
206 | * currently showing page's number.
207 | * @returns {number} the page number
208 | */
209 | getPage() {
210 | return this.$scope.pageNo;
211 | }
212 |
213 | /**
214 | * Adds animation to slicing (page or view changes)
215 | * By adding class to the carrier DOM element
216 | * Actual animations are handles in CSS.
217 | */
218 | animateSlicing() {
219 | let elemGridHolder = document.getElementById('gridHolder');
220 | if (elemGridHolder) {
221 | document.getElementById('gridHolder').className = 'gridHolder gridSlicingAnim';
222 | this.$timeout(() => {
223 | elemGridHolder.className = 'gridHolder gridNoAnim';
224 | }, 200);
225 | }
226 | }
227 |
228 | pushToCurrentPhrase(wordObj2Push, speak) {
229 | let wo2p = JSON.parse(JSON.stringify(wordObj2Push));
230 | let cp = this.$scope.global.currentPhrase;
231 |
232 | // Update word with custom languagePack function.
233 | wo2p = this.conj.beforePushing(wo2p, cp);
234 |
235 | this.$scope.global.currentPhrase.push(wo2p);
236 | if (speak == true) {
237 | this.tts.speak(wo2p.title);
238 | }
239 | }
240 |
241 | /**
242 | * Closes cover on clicking on it!
243 | */
244 | clickCover() {
245 | let cover = document.getElementById("symbolCover");
246 | if (cover) {
247 | cover.style.display = "none";
248 | }
249 | this.$scope.global.isDraggable = true;
250 | }
251 | }
252 | // Service Dependency Injection
253 | GridController.$inject = ['$scope', '$global', '$timeout', 'TTSManager', 'ConjunctionManager'];
254 |
--------------------------------------------------------------------------------
/app/components/grid/grid.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |

8 |
{{previousText}}
9 |
10 |
11 |
12 |

13 |
{{backText}}
14 |
15 |
16 |
17 |
18 |
19 |

20 |
{{nextText}}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/components/grid/grid.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular'
2 |
3 | import recentModule from 'components/recent/recent'
4 | import symbolModule from 'components/symbol/symbol'
5 | import keyboardModule from 'components/keyboard/keyboard'
6 | import GridController from './grid-controller'
7 |
8 | let gridModule = angular.module('aacApp.grid', [recentModule.name, symbolModule.name, keyboardModule.name]);
9 |
10 | gridModule.directive('grid', function() {
11 | return {templateUrl: 'components/grid/grid.html'};
12 | });
13 | gridModule.controller('grid-controller', GridController);
14 |
15 | export default gridModule;
16 |
--------------------------------------------------------------------------------
/app/components/header/header-controller.js:
--------------------------------------------------------------------------------
1 | import {capitalize} from '../../js/utils'
2 | import * as CONSTANT from '../../js/constants'
3 | import otsimo from 'otsimo';
4 |
5 | /**
6 | *
7 | *
8 | * @export
9 | * @class HeaderController
10 | */
11 | export default class HeaderController {
12 | /**
13 | * Creates an instance of HeaderController.
14 | *
15 | * @param {any} $scope
16 | * @param {any} $global
17 | * @param {any} $timeout
18 | *
19 | * @memberOf HeaderController
20 | */
21 | constructor($scope, $global, $timeout) {
22 | console.log("HEADER MOUNTED");
23 | this.$scope = $scope;
24 | this.global = $global;
25 | this.$timeout = $timeout;
26 | // Initilize variables for controller.
27 |
28 | // Call controllerInit
29 | this.controllerInit();
30 | }
31 | /**
32 | * Create or facilitate new functions for $scope or $global service.
33 | */
34 | controllerInit() {
35 | this.global.changeCurrentTab = this.changeCurrentTab.bind(this);
36 | }
37 |
38 | /**
39 | * This function handles the text and variable changes
40 | * on the view when the tab is changed.
41 | * Also calles changeTab function.
42 | *
43 | * @param {string} tabExp - new tab name
44 | */
45 | changeCurrentTab(tabExp) {
46 | if (tabExp === CONSTANT.TAB_MAIN) {
47 | this.global.currentPage = this.$scope.pageText1;
48 | this.global.isHome = 1;
49 | } else if (tabExp === CONSTANT.TAB_GROUP) {
50 | this.global.currentPage = this.$scope.pageText2 + capitalize(this.global.currentGroup);
51 | this.global.isHome = 0;
52 | } else if (tabExp === CONSTANT.TAB_DERIVABLE) {
53 | this.global.currentPage = this.$scope.pageText3 + capitalize(this.global.currentDerivable);
54 | this.global.isHome = 0;
55 | } else if (tabExp === CONSTANT.TAB_RECENT) {
56 | this.global.currentPage = this.$scope.pageText4;
57 | this.global.isHome = 0;
58 | } else if (tabExp === CONSTANT.TAB_KEYBOARD) {
59 | this.global.currentPage = this.$scope.pageText5;
60 | this.global.isHome = 0;
61 | }
62 |
63 | if (tabExp === CONSTANT.TAB_RECENT || tabExp === CONSTANT.TAB_KEYBOARD) {
64 | this.global.currentTab = tabExp;
65 | } else {
66 | this.global.currentTab = CONSTANT.TAB_MAIN;
67 | this.global.changeTab(tabExp);
68 | }
69 | }
70 |
71 | /**
72 | * Opens recent tab.
73 | */
74 | openRecent() {
75 | this.changeCurrentTab(CONSTANT.TAB_RECENT);
76 | // Animate Recent Icon
77 | this.animIconTouch('rIcon');
78 | }
79 | /**
80 | * Initilize Going the entrance screen when;
81 | * Current tab is not main or,
82 | * The page is not 0 or,
83 | * there is a currentGroup or currentDerivable
84 | */
85 | goHome() {
86 | let gCarrier = document.getElementById("generalCarrier");
87 | if (gCarrier) {
88 | gCarrier.style.height = "100%";
89 | }
90 | if (this.global.currentTab !== CONSTANT.TAB_MAIN || this.global.getPage() !== 0 || this.global.currentGroup || this.global.currentDerivable) {
91 | // Animate Home Icon
92 | this.animIconTouch('hIcon');
93 |
94 | this.global.changeCurrentTab(CONSTANT.TAB_MAIN);
95 | this.global.currentGroup = '';
96 | this.global.currentDerivable = '';
97 | this.global.go2FirstPage();
98 | this.updateGridQuantity();
99 | }
100 | }
101 | /**
102 | * Opens the grid page
103 | */
104 | openGrid() {
105 | let gCarrier = document.getElementById("generalCarrier");
106 | if (gCarrier) {
107 | gCarrier.style.height = "100%";
108 | }
109 | if (this.global.currentTab !== CONSTANT.TAB_MAIN) {
110 | // Animate Grid Icon
111 | this.animIconTouch('gIcon');
112 | this.global.changeCurrentTab(CONSTANT.TAB_MAIN);
113 | }
114 | }
115 | /**
116 | * Opens the keyboard page
117 | */
118 | openKeyboard() {
119 | let gCarrier = document.getElementById("generalCarrier");
120 | if (gCarrier) {
121 | gCarrier.style.height = "2000px";
122 | }
123 | if (this.global.currentTab !== CONSTANT.TAB_KEYBOARD) {
124 | // Animate Grid Icon
125 | this.animIconTouch('kIcon');
126 | this.global.changeCurrentTab(CONSTANT.TAB_KEYBOARD);
127 | }
128 | }
129 | /**
130 | * Quits the application and turns back to Otsimo app hub if the isHome is true.
131 | * Else, get the app to home (goHome)
132 | */
133 | quitGame() {
134 | // Special animation for Back icon.
135 | let backIconElem = document.getElementById('bIcon');
136 | if (backIconElem) {
137 | backIconElem.className = 'backIcon backIconHovered';
138 | this.$timeout(() => backIconElem.className = 'backIcon', 300);
139 | }
140 | if (this.global.isHome === 1) {
141 | otsimo.quitgame();
142 | } else {
143 | this.goHome();
144 | }
145 | }
146 | /**
147 | * Does animation for icon's DOM that is given.
148 | *
149 | * @param {string} iconId - the icon id
150 | */
151 | animIconTouch(iconId) {
152 | let IconElem = document.getElementById(iconId);
153 | if (IconElem) {
154 | IconElem.className = 'material-icons iconOpacity';
155 | this.$timeout(() => IconElem.className = 'material-icons', 300);
156 | }
157 | }
158 |
159 | /**
160 | * Updates the quantity of the symbols in the grid
161 | * with respect to the current page/view.
162 | */
163 | updateGridQuantity() {
164 | let x = this.global.gridSize[0];
165 | let y = this.global.gridSize[1];
166 | let gridQuantity;
167 | if (this.global.currentTab !== CONSTANT.TAB_MAIN) {
168 | gridQuantity = (x * y) - 1;
169 | } else {
170 | if (this.$scope.pageNo === 0) {
171 | gridQuantity = x * y;
172 | } else {
173 | gridQuantity = (x * y) - 1;
174 | }
175 | }
176 | this.global.gridQuantity = gridQuantity;
177 | }
178 |
179 | }
180 |
181 | // Service Dependency Injection
182 | HeaderController.$inject = ['$scope', '$global', '$timeout'];
183 |
--------------------------------------------------------------------------------
/app/components/header/header-test.js:
--------------------------------------------------------------------------------
1 | /* eslint-env jasmine */
2 | import { module, inject } from 'mocks'
3 | import HeaderController from './header-controller'
4 | import GridController from '../grid/grid-controller'
5 | import header from './header'
6 | import Global from '../../services/global'
7 | import LSManager from '../../services/localstorage'
8 |
9 | describe('aacApp.header module', () => {
10 | beforeEach(module(header.name));
11 |
12 | let $controller;
13 | let headerCtrl;
14 | let g, ls, tts, cm;
15 | beforeEach(inject((_$controller_) => {
16 | // The injector unwraps the underscores (_) from around the parameter names when matching
17 | $controller = _$controller_;
18 | }));
19 |
20 | beforeEach(() => {
21 | g = new Global();
22 | g.changeTab = function() {}
23 | ls = new LSManager();
24 | g.pushToCurrentPhrase = function(obj) {
25 | headerCtrl.$scope.global.currentPhrase.push(obj);
26 | }
27 | tts = { speak: function() {} };
28 | cm = {
29 | englishConjunctor: function() {},
30 | turkishConjunctor: function() {},
31 | addPossessiveEn: function() {},
32 | addPossessiveTr: function() {}
33 | };
34 | headerCtrl = $controller(HeaderController, { $scope: {}, $global: g });
35 | $controller(GridController, { $scope: {}, $global: g, LSManager: ls, TTSManager: tts, ConjunctionManager: cm });
36 | });
37 |
38 | describe('header controller', () => {
39 | it('should be defined', () => {
40 | expect(headerCtrl)
41 | .toBeDefined();
42 | });
43 | });
44 |
45 | describe('currentTab', () => {
46 | it('should be equal to recent after calling openRecent function', () => {
47 | headerCtrl.openRecent();
48 | expect(headerCtrl.$scope.global.currentTab)
49 | .toBe('recent');
50 | expect(headerCtrl.$scope.global.isHome)
51 | .toBe(0);
52 | });
53 |
54 | it('should be equal to main after calling openGrid function', () => {
55 | headerCtrl.openGrid();
56 | expect(headerCtrl.$scope.global.currentTab)
57 | .toBe('main');
58 | });
59 |
60 | it('should be equal to main after calling goHome function', () => {
61 | headerCtrl.goHome();
62 | expect(headerCtrl.$scope.global.currentTab)
63 | .toBe('main');
64 | expect(headerCtrl.$scope.global.isHome)
65 | .toBe(1);
66 | });
67 |
68 | });
69 |
70 | describe('pageNo', () => {
71 | it('should be equal to 0 after calling goHome function', () => {
72 | headerCtrl.goHome();
73 | expect(headerCtrl.$scope.global.go2FirstPage())
74 | .toBe(0);
75 | });
76 | });
77 |
78 | describe('currentGroup', () => {
79 | it('should be empty string after calling goHome f', () => {
80 | headerCtrl.goHome();
81 | expect(headerCtrl.$scope.global.currentGroup)
82 | .toBe('');
83 | });
84 | });
85 |
86 | describe('currentDerivable', () => {
87 | it('should be empty string after calling goHome f', () => {
88 | headerCtrl.goHome();
89 | expect(headerCtrl.$scope.global.currentDerivable)
90 | .toBe('');
91 | });
92 | });
93 |
94 | });
95 |
--------------------------------------------------------------------------------
/app/components/header/header.html:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/app/components/header/header.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import HeaderController from './header-controller';
3 |
4 | let headerModule = angular.module('aacApp.header', []);
5 |
6 | headerModule.directive('header', function () {
7 | return {
8 | templateUrl: 'components/header/header.html'
9 | };
10 | });
11 |
12 | headerModule.controller('header-controller', HeaderController);
13 |
14 | export default headerModule;
15 |
--------------------------------------------------------------------------------
/app/components/keyboard/keyboard-controller.js:
--------------------------------------------------------------------------------
1 | import { returnTime, updateCurrentPhraseScroll } from "js/utils";
2 |
3 | /**
4 | * KeyboardController
5 | * @export
6 | * @class KeyboardController
7 | */
8 | export default class KeyboardController {
9 | /**
10 | * Creates an instance of KeyboardController.
11 | *
12 | * @param {any} $scope
13 | * @param {any} $global
14 | * @param {any} $timeout
15 | * @param {TTSManager} TTSManager
16 | *
17 | * @memberOf RecentController
18 | */
19 | constructor($scope, $global, $timeout, EventManager, TTSManager) {
20 | this.$scope = $scope;
21 | this.$scope.global = $global;
22 | this.$timeout = $timeout;
23 | this.tts = TTSManager;
24 | this.event = EventManager;
25 | this.fastTypeTimer = null;
26 | this.suggestions = false;
27 | // Initilize variables for controller.
28 |
29 | // Call controllerInit
30 | this.controllerInit();
31 | }
32 |
33 | /**
34 | * Create or facilitate new functions for $scope or $global service.
35 | */
36 | controllerInit() {
37 | this.phraseIndex = 0;
38 | this.suggestionList = [];
39 | }
40 |
41 | /**
42 | * Shows the keyboard
43 | */
44 | showKeyboard() {
45 | let typeInput = document.getElementById("typeInput");
46 | if (typeInput) {
47 | typeInput.focus();
48 | }
49 | }
50 | /**
51 | * Removes readonly from typeInput
52 | * Triggered when type input is
53 | * Walkaround for ios autocorrect
54 | */
55 | showKeyboardFocused(e) {
56 | let inpCarrier = document.querySelector(".inputCarrier");
57 | let typeInput = document.getElementById("typeInput");
58 | if (typeInput) {
59 | e.preventDefault();
60 | e.stopPropagation();
61 | let headerHeight = document.getElementById("header").clientHeight;
62 | let phHeight = document.querySelector(".phraseHolder").clientHeight;
63 |
64 | document.addEventListener("touchmove", function(ev) {
65 | ev.preventDefault();
66 | });
67 |
68 | this.$timeout(() => {
69 | e.preventDefault();
70 | e.stopPropagation();
71 | // inpCarrier.style.top = parseInt(window.innerHeight - (headerHeight + phHeight + 70)) + 'px';
72 | window.scrollTo(0, 0);
73 | console.log("virtual keyboard opened!");
74 | this.showHideSuggestions(true);
75 | }, 500);
76 | }
77 | }
78 |
79 | /**
80 | * Hides the keyboard
81 | */
82 | hideKeyboard() {
83 | let inpCarrier = document.querySelector(".inputCarrier");
84 | let typeInput = document.getElementById("typeInput");
85 | if (typeInput) {
86 | document.getElementById("generalCarrier").style.height = "100%";
87 | inpCarrier.style.top = "0px";
88 | this.showHideSuggestions(false);
89 | }
90 | }
91 |
92 | /**
93 | * Shows/Hides the suggestionList
94 | */
95 | showHideSuggestions(status) {
96 | let suggestionList = document.getElementById("suggestionList");
97 | if (suggestionList) {
98 | suggestionList.style.opacity = status ? "1" : "0";
99 | }
100 | }
101 |
102 | /**
103 | * Submits (handles) the current typeInput string content.
104 | */
105 | submitCurrentInput() {
106 | let typeInput = document.getElementById("typeInput");
107 | if (typeInput.value) {
108 | let inputWord = {};
109 | inputWord.title = typeInput.value.toLocaleLowerCase();
110 | let getFromMap = this.$scope.global.extendedSlugMap[inputWord.title];
111 | if (getFromMap) {
112 | inputWord.slug = getFromMap;
113 | } else {
114 | inputWord.slug = inputWord.title;
115 | }
116 | let checkExist = this.checkWordInDB(inputWord.slug);
117 | inputWord.slugExist = !checkExist;
118 |
119 | if (inputWord.title.contains(" ") && !checkExist) {
120 | this.recognizeWord(inputWord.title);
121 | } else {
122 | inputWord.phraseIndex = this.phraseIndex++;
123 | this.$scope.global.pushToCurrentPhrase(inputWord);
124 | }
125 | updateCurrentPhraseScroll();
126 | this.tts.speak(inputWord.title);
127 | let gs = this.$scope.global.gridSize;
128 | this.event.appWord(inputWord.title, gs[0], gs[1], true);
129 | typeInput.value = "";
130 | }
131 | }
132 |
133 | /**
134 | * Handles the submission of the input via "enter" from the device
135 | */
136 | enterSubmit($event) {
137 | if ($event.keyCode === 13) {
138 | this.submitCurrentInput();
139 | this.suggestionList = [];
140 | } else {
141 | let typeInput = document.getElementById("typeInput");
142 | if (typeInput) {
143 | // If there is a timeout, clear it.
144 | if (this.fastTypeTimer) {
145 | this.$timeout.cancel(this.fastTypeTimer);
146 | }
147 | this.fastTypeTimer = this.$timeout(() => {
148 | if (typeInput.value.length > 1) {
149 | this.suggestWordsByInput(typeInput.value);
150 | } else {
151 | this.suggestionList = [];
152 | this.suggestions = false;
153 | }
154 | }, 300);
155 | }
156 | }
157 | }
158 |
159 | /**
160 | * Handles, creates the data to set suggestions by,
161 | * current keyboard input.
162 | */
163 | suggestWordsByInput(searchLetter) {
164 | this.suggestions = true;
165 | searchLetter = searchLetter.toLocaleLowerCase();
166 | //searchLetter = searchLetter.replace(" ", "-");
167 | // Might need replace all?
168 | this.suggestionList = this.$scope.global.extendedTitleArray.filter(word => {
169 | if (word) {
170 | return word.substring(0, searchLetter.length) == searchLetter;
171 | }
172 | });
173 | if (this.suggestionList.length > 0) {
174 | this.suggestionList = this.suggestionList.filter(
175 | (it, i, ar) => ar.indexOf(it) === i,
176 | );
177 | this.suggestionList.sort(this.sortByLength);
178 | }
179 | }
180 |
181 | /**
182 | * Handles actions when a suggestion symbol is clicked
183 | */
184 | clickSuggestion(wordSlug) {
185 | let wordObj2Push = {};
186 | wordObj2Push.title = wordSlug.replace("-", " ");
187 | wordObj2Push.slug = this.$scope.global.extendedSlugMap[wordSlug];
188 | if (this.isVerb(wordSlug)) {
189 | wordObj2Push.type = "verb";
190 | }
191 | wordObj2Push.phraseIndex = this.phraseIndex++;
192 | this.$scope.global.pushToCurrentPhrase(wordObj2Push);
193 | this.tts.speak(wordObj2Push.title);
194 | let typeInput = document.getElementById("typeInput");
195 | if (typeInput) {
196 | document.getElementById("typeInput").value = "";
197 | }
198 | this.suggestionList = [];
199 | this.suggestions = false;
200 | }
201 |
202 | /**
203 | * Recognizes the string given by the current word package before pushing.
204 | * Then pushes the pieces indivicually.
205 | */
206 | recognizeWord(word) {
207 | // this function can be better algorithmicly!
208 | let splittedUnrecognizedWord = word.split(" ");
209 | splittedUnrecognizedWord.forEach(wordPiece => {
210 | if (wordPiece) {
211 | let wordObj2Push = {};
212 | wordObj2Push.title = wordPiece;
213 | let getFromMap = this.$scope.global.extendedSlugMap[wordPiece];
214 | if (getFromMap) {
215 | wordObj2Push.slug = getFromMap;
216 | } else {
217 | wordObj2Push.slug = wordPiece;
218 | }
219 | wordObj2Push.slugExist = !this.checkWordInDB(wordObj2Push.slug);
220 | wordObj2Push.phraseIndex = this.phraseIndex++;
221 | this.$scope.global.pushToCurrentPhrase(wordObj2Push);
222 | }
223 | });
224 | }
225 |
226 | /**
227 | * Checks if the given word in the mainSlugArray
228 | */
229 | checkWordInDB(word) {
230 | return this.$scope.global.extendedSlugArray.contains(word);
231 | }
232 |
233 | /**
234 | * Checks if the given word is a verb.
235 | */
236 | isVerb(word) {
237 | let words = this.$scope.global.mainArray.filter(w => {
238 | return w.slug == word;
239 | });
240 | if (words.length > 0) {
241 | if (words[0].type == "verb") {
242 | return true;
243 | } else {
244 | return false;
245 | }
246 | } else {
247 | return false;
248 | }
249 | }
250 |
251 | /**
252 | * Turn suggested word into its slug
253 | */
254 | slugSuggest(suggestWord) {
255 | return this.$scope.global.extendedSlugMap[suggestWord];
256 | }
257 |
258 | /**
259 | * Comparission function for .sort() prototype.
260 | */
261 | sortByLength(a, b) {
262 | if (a.length > b.length) {
263 | return 1;
264 | }
265 | if (a.length < b.length) {
266 | return -1;
267 | }
268 | return 0;
269 | }
270 | }
271 |
272 | // Service Dependency Injection
273 | KeyboardController.$inject = [
274 | "$scope",
275 | "$global",
276 | "$timeout",
277 | "EventManager",
278 | "TTSManager",
279 | ];
280 |
--------------------------------------------------------------------------------
/app/components/keyboard/keyboard.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
![]()
5 |
{{suggestionWord}}
6 |
7 |
8 |
9 |
10 |
12 | check_circle
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/components/keyboard/keyboard.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import KeyboardController from './keyboard-controller'
3 |
4 | let keyboardModule = angular.module('aacApp.keyboard', []);
5 |
6 | keyboardModule.directive('keyboard', function () {
7 | return {
8 | templateUrl: 'components/keyboard/keyboard.html'
9 | };
10 | });
11 |
12 | keyboardModule.controller('keyboard-controller', KeyboardController);
13 |
14 | export default keyboardModule;
15 |
--------------------------------------------------------------------------------
/app/components/phrase/phrase-controller.js:
--------------------------------------------------------------------------------
1 | /**
2 | * PhraseController
3 | * @export
4 | * @class PhraseController
5 | */
6 | export default class PhraseController {
7 | /**
8 | * Creates an instance of PhraseController.
9 | *
10 | * @param {any} $scope
11 | * @param {any} $global
12 | * @param {any} $timeout
13 | * @param {EventManager} EventManager
14 | * @param {TTSManager} TTSManager
15 | * @param {LSManager} LSManager
16 | */
17 | constructor($scope, $global, $timeout, EventManager, TTSManager, LSManager) {
18 | this.$scope = $scope;
19 | this.$scope.global = $global;
20 | this.$timeout = $timeout;
21 | this.events = EventManager;
22 | this.tts = TTSManager;
23 | this.ls = LSManager;
24 | // Initilize variables for controller.
25 | this.bstouchTimer = null;
26 | this.phraseTouchTimer = null;
27 | this.beforeTouch = 0;
28 | this.willActivateDraggable = true;
29 | this.activatedFirstTime = false;
30 | this.dragListener = {
31 | allowDuplicates: true,
32 | }
33 |
34 | // Call controllerInit
35 | this.controllerInit();
36 | }
37 | /**
38 | * Create or facilitate new functions for $scope or $global service.
39 | */
40 | controllerInit() {}
41 |
42 | /**
43 | * Remove Last Word from the current phrase.
44 | */
45 | removeLastWord() {
46 | this.$scope.global.currentPhrase.pop();
47 | }
48 | /**
49 | * Submit - Save to History - Play the current phrase.
50 | */
51 | submitPhrase() {
52 | if (this.$scope.global.currentPhrase.length > 0) {
53 | let i = 0;
54 | let currentPhraseString = '';
55 | this.$scope.currentPhraseTransition = 'cpTransition';
56 | this.ls.addPhrase2History(this.$scope.global.currentPhrase);
57 |
58 | this.$timeout(() => this.$scope.currentPhraseTransition = '', 300);
59 | while (i < this.$scope.global.currentPhrase.length) {
60 | currentPhraseString = currentPhraseString + this.$scope.global.currentPhrase[i].title + ' ';
61 | i++;
62 | }
63 | //Send phrase as a string to otsimo.tts with spaces between words.
64 | this.tts.speak(currentPhraseString);
65 | this.events.appPhrase(currentPhraseString);
66 | }
67 | }
68 |
69 | phraseTouchStart(e, cp) {
70 | let cPhrase = (!cp) ? document.getElementById("cPhrase") : cp;
71 | if (cPhrase) {
72 | this.beforeTouch = cPhrase.scrollLeft;
73 | }
74 |
75 | this.phraseTouchTimer = this.$timeout(() => {
76 | if (Math.abs(this.beforeTouch - cPhrase.scrollLeft) > 20) {
77 | this.willActivateDraggable = false;
78 | } else {
79 | this.activatedFirstTime = true;
80 | this.activateDraggable();
81 | }
82 | }, 1000);
83 | }
84 |
85 | phraseTouchEnd(e) {
86 | if (this.phraseTouchTimer) {
87 | this.$timeout.cancel(this.phraseTouchTimer);
88 |
89 | let status = this.phraseTouchTimer.$$state.status;
90 | if (status == 1 && this.willActivateDraggable) {
91 | this.activateDraggable();
92 | this.willActivateDraggable = false;
93 | } else if (status == 2) {
94 | if (this.$scope.global.isDraggable) {
95 | let cPhrase = document.getElementById("cPhrase");
96 | if (this.beforeTouch == cPhrase.scrollLeft) {
97 | this.submitPhrase();
98 | }
99 | }
100 | }
101 | }
102 | }
103 |
104 | activateDraggable() {
105 | this.$scope.global.isDraggable = false;
106 | let cover = document.getElementById("symbolCover");
107 | if (cover) {
108 | cover.style.display = "block";
109 | }
110 | }
111 |
112 |
113 | /**
114 | * TouchStart function for backspace button.
115 | * Check backspace hold action in 500 m-secs.
116 | */
117 | bsTouchStart() {
118 | let bsElem = document.getElementById('bs');
119 | if (bsElem) {
120 | bsElem.style.color = this.$scope.removeHoldColor;
121 | }
122 | this.bstouchTimer = this.$timeout(() => {
123 | this.$scope.global.currentPhrase.splice(0, this.$scope.global.currentPhrase.length);
124 | }, 500);
125 | }
126 |
127 | /**
128 | * TouchEnd function for backspace button.
129 | * Clear the timeout for removing all words from current phrase.
130 | */
131 | bsTouchEnd() {
132 | let bsElem = document.getElementById('bs');
133 | if (bsElem) {
134 | bsElem.style.color = this.$scope.removeNormalColor;
135 | }
136 | this.$timeout.cancel(this.bstouchTimer);
137 | }
138 | }
139 |
140 | // Service Dependency Injection
141 | PhraseController.$inject = ['$scope', '$global', '$timeout', 'EventManager', 'TTSManager', 'LSManager'];
142 |
--------------------------------------------------------------------------------
/app/components/phrase/phrase.html:
--------------------------------------------------------------------------------
1 |
2 |
21 |
23 | backspace
24 |
25 |
--------------------------------------------------------------------------------
/app/components/phrase/phrase.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import PhraseController from './phrase-controller'
3 |
4 | let phraseModule = angular.module('aacApp.phrase', []);
5 |
6 | phraseModule.directive('phrase', function () {
7 | return {
8 | templateUrl: 'components/phrase/phrase.html'
9 | };
10 | });
11 |
12 | phraseModule.controller('phrase-controller', PhraseController);
13 |
14 | export default phraseModule;
15 |
--------------------------------------------------------------------------------
/app/components/recent/recent-controller.js:
--------------------------------------------------------------------------------
1 | import { returnTime } from 'js/utils';
2 |
3 | /**
4 | * RecentController
5 | * @export
6 | * @class RecentController
7 | */
8 | export default class RecentController {
9 | /**
10 | * Creates an instance of RecentController.
11 | *
12 | * @param {any} $scope
13 | * @param {any} $global
14 | * @param {any} EventManager
15 | *
16 | * @memberOf RecentController
17 | */
18 | constructor($scope, $global, EventManager, LSManager) {
19 | this.$scope = $scope;
20 | this.$scope.global = $global;
21 | this.events = EventManager;
22 | this.ls = LSManager;
23 |
24 | // Initilize variables for controller.
25 | this.bstouchTimer;
26 |
27 | // Call controllerInit
28 | this.controllerInit();
29 | }
30 |
31 | /**
32 | * Create or facilitate new functions for $scope or $global service.
33 | */
34 | controllerInit() {
35 | this.$scope.global.recentPhrases = this.ls.getHistoryAsArray();
36 | // Open 'last 30 min' History interval then controller initilized.
37 | this.changeInterval(1);
38 | }
39 |
40 | /**
41 | * Loads a prior phrase from history to the current phrase.
42 | * If there is; Adds the old phrase to end of the current phrase.
43 | * @param {number} index - the index
44 | */
45 | loadRecentPhrase(index) {
46 | let phraseHistory = this.$scope.recentPhrasesList;
47 | let phrase2Add = phraseHistory[phraseHistory.length - (index + 1)].phrase;
48 | this.$scope.global.currentPhrase = this.$scope.global.currentPhrase.concat(phrase2Add);
49 | }
50 |
51 | /**
52 | * Changes the showing history interval to stated interval.
53 | * @param {number} val The value which can be 1,2,3,4
54 | */
55 | changeInterval(val) {
56 | // 1 -> last 30 min;
57 | // 2 -> Today
58 | // 3 -> Yesterday
59 | // 4 -> Last Week
60 | let timeH;
61 | let timeC = returnTime();
62 | let timeL;
63 | const halfHour = 1000 * 60 * 30;
64 | const oneDay = halfHour * 2 * 24;
65 | const oneWeek = oneDay * 7;
66 | if (val === 1) {
67 | timeH = timeC;
68 | timeL = timeC - halfHour;
69 | } else if (val === 2) {
70 | timeH = timeC - halfHour;
71 | timeL = timeC - oneDay;
72 | } else if (val === 3) {
73 | timeH = timeC - oneDay;
74 | timeL = timeC - oneDay * 2;
75 | } else if (val === 4) {
76 | timeH = timeC - oneDay * 2;
77 | timeL = timeC - oneWeek;
78 | }
79 | if (this.$scope.global.recentPhrases) {
80 | this.$scope.recentPhrasesList = this.$scope.global.recentPhrases
81 | .filter((phraseRecent) => phraseRecent.time < timeH && phraseRecent.time > timeL);
82 | }
83 | this.events.appInterval(val);
84 | }
85 | }
86 |
87 | // Service Dependency Injection
88 | RecentController.$inject = ['$scope', '$global', 'EventManager', 'LSManager'];
89 |
--------------------------------------------------------------------------------
/app/components/recent/recent-test.js:
--------------------------------------------------------------------------------
1 | /* eslint-env jasmine */
2 | import { module, inject } from 'mocks';
3 | import RecentController from './recent-controller';
4 | import recentModule from './recent';
5 | import * as CONSTANT from '../../js/constants';
6 | import Global from '../../services/global';
7 | import LSManager from '../../services/localstorage';
8 |
9 | function randomString(len, charSet) {
10 | charSet = charSet || 'abcdefghijklmnopqrstuvwxyz';
11 | let randomString = '';
12 | for (let i = 0; i < len; i++) {
13 | let randomPoz = Math.floor(Math.random() * charSet.length);
14 | randomString += charSet.substring(randomPoz, randomPoz + 1);
15 | }
16 | return randomString;
17 | }
18 |
19 | function generateSymbol(length, normal, derivable, group) {
20 | let classes = [];
21 | if (normal) { classes.push(CONSTANT.CLASS_MAIN); }
22 | if (derivable) { classes.push(CONSTANT.CLASS_DERIVABLE); }
23 | if (group) { classes.push(CONSTANT.CLASS_GROUP); }
24 | let i = 0;
25 | let symbArray = [];
26 | while (i < length) {
27 | let randString = randomString(7);
28 | let wordObj = {
29 | title: randString,
30 | slug: randString,
31 | type: 'noun',
32 | parent: 'main'
33 | };
34 | wordObj.class = classes[Math.floor(Math.random() * (classes.length - 1)) + 1];
35 | symbArray.push(wordObj);
36 | i++;
37 | }
38 | return symbArray;
39 | }
40 |
41 | function generatePhraseHistory(length) {
42 | let symbPhraseArray = [];
43 | let i = 0;
44 | while (i < length) {
45 | let randString = randomString(7);
46 | let phraseObj = {
47 | phrase: generateSymbol(3, 1, 1, 0),
48 | time: 1473336150685
49 | };
50 | symbPhraseArray.push(phraseObj);
51 | i++;
52 | }
53 | return symbPhraseArray;
54 | }
55 |
56 | describe('aacApp.phrase module', () => {
57 | beforeEach(module(recentModule.name));
58 |
59 | let $controller;
60 | let recentCtrl;
61 | let g, event, ls;
62 | beforeEach(inject((_$controller_) => {
63 | // The injector unwraps the underscores (_) from around the parameter names when matching
64 | $controller = _$controller_;
65 | }));
66 |
67 | beforeEach(() => {
68 | g = new Global();
69 | ls = new LSManager();
70 | event = { appInterval: function () {} };
71 | g.changeTab = function () {}
72 | //$controller(dependentController, {$scope:{},$global:g});
73 | });
74 |
75 | describe('recent controller', () => {
76 | it('should be defined', () => {
77 | recentCtrl = $controller(RecentController, { $scope: {}, $global: g, EventManager: event, LSManager: ls });
78 | expect(recentCtrl)
79 | .toBeDefined();
80 | });
81 | });
82 |
83 | describe('loadRecentPhrase', () => {
84 | it('should pushes the the prior phrase with given index', () => {
85 | g.currentPhrase = generateSymbol(2, 1, 1, 0);
86 | g.recentPhrases = generatePhraseHistory(5);
87 | recentCtrl = $controller(RecentController, { $scope: {}, $global: g, EventManager: event, LSManager: ls });
88 | recentCtrl.$scope.recentPhrasesList = generatePhraseHistory(10);
89 | let index = 0;
90 | while (index < recentCtrl.$scope.recentPhrasesList.length) {
91 | recentCtrl.$scope.global.currentPhrase = generateSymbol(2, 1, 1, 1);
92 | recentCtrl.loadRecentPhrase(index);
93 | let cp = recentCtrl.$scope.global.currentPhrase;
94 | let addition2CurrentPhrase = cp.slice(2, cp.length);
95 | let rp = recentCtrl.$scope.recentPhrasesList;
96 | let loadedPhrase = rp[rp.length - (index + 1)].phrase;
97 | expect(JSON.stringify(addition2CurrentPhrase))
98 | .toBe(JSON.stringify(loadedPhrase));
99 | index++;
100 | }
101 | });
102 | });
103 |
104 |
105 | describe('recentPhrases', () => {
106 | it('should have at least 1 symbol element inside', () => {
107 | let i = 0;
108 | recentCtrl.$scope.global.currentPhrase = generateSymbol(2, 1, 1, 1);
109 | recentCtrl.$scope.global.recentPhrases.forEach(() => {
110 | expect(recentCtrl.$scope.global.recentPhrases[i].phrase.length)
111 | .toBeGreaterThan(0);
112 | i++;
113 | });
114 | });
115 | });
116 |
117 | describe('changeInterval', () => {
118 | it('should filter recentPhrases to the given time interval', () => {
119 | g.currentPhrase = generateSymbol(2, 1, 1, 0);
120 | g.recentPhrases = generatePhraseHistory(5);
121 | recentCtrl = $controller(RecentController, { $scope: {}, $global: g, EventManager: event, LSManager: ls });
122 | recentCtrl.$scope.recentPhrasesList = generatePhraseHistory(10);
123 | recentCtrl.changeInterval(1);
124 | expect(recentCtrl.$scope.recentPhrasesList.length)
125 | .toMatch(/\d{1,}/);
126 | recentCtrl.changeInterval(2);
127 | expect(recentCtrl.$scope.recentPhrasesList.length)
128 | .toMatch(/\d{1,}/);
129 | recentCtrl.changeInterval(3);
130 | expect(recentCtrl.$scope.recentPhrasesList.length)
131 | .toMatch(/\d{1,}/);
132 | recentCtrl.changeInterval(4);
133 | expect(recentCtrl.$scope.recentPhrasesList.length)
134 | .toMatch(/\d{1,}/);
135 | });
136 | });
137 |
138 |
139 |
140 | });
141 |
--------------------------------------------------------------------------------
/app/components/recent/recent.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - {{timeIntervalText1}}
5 | - {{timeIntervalText2}}
6 | - {{timeIntervalText3}}
7 | - {{timeIntervalText4}}
8 |
9 |
10 |
11 |
12 |
27 |
28 |
--------------------------------------------------------------------------------
/app/components/recent/recent.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import RecentController from './recent-controller'
3 |
4 | let recentModule = angular.module('aacApp.recent', []);
5 |
6 | recentModule.directive('recent', function () {
7 | return {
8 | templateUrl: 'components/recent/recent.html'
9 | };
10 | });
11 |
12 | recentModule.controller('recent-controller', RecentController);
13 |
14 | export default recentModule;
15 |
--------------------------------------------------------------------------------
/app/components/symbol/symbol-controller.js:
--------------------------------------------------------------------------------
1 | import { updateCurrentPhraseScroll } from "../../js/utils";
2 | import * as CONSTANT from "../../js/constants";
3 |
4 | /**
5 | * The SymbolController
6 | * @export
7 | * @class SymbolController
8 | */
9 | export default class SymbolController {
10 | /**
11 | * Creates an instance of SymbolController.
12 | *
13 | * @param {any} $scope
14 | * @param {any} $http
15 | * @param {any} $timeout
16 | * @param {any} $global
17 | * @param {EventManager} EventManager
18 | *
19 | * @memberOf SymbolController
20 | */
21 | constructor(
22 | $scope,
23 | $http,
24 | $timeout,
25 | $global,
26 | EventManager,
27 | ConjunctionManager,
28 | ) {
29 | this.$scope = $scope;
30 | this.$scope.global = $global;
31 | this.$timeout = $timeout;
32 | this.events = EventManager;
33 | this.conj = ConjunctionManager;
34 | // Initilize variables for controller.
35 | this.wordTouchTimer = null;
36 | this.currentlyHolding = null;
37 | }
38 |
39 | /**
40 | * Called when user starts touch action.
41 | * Handles hold actions if user holds on symbol 300ms
42 | * @param {Object} wordObj
43 | * @param {number} index
44 | */
45 | wordTouchStart(wordObj, index) {
46 | this.currentlyHolding = wordObj.title;
47 | this.wordTouchTimer = this.$timeout(() => {
48 | if (wordObj.class === CONSTANT.CLASS_DERIVABLE) {
49 | this.$scope.global.currentDerivable = wordObj.slug;
50 | this.events.appDerive(wordObj.slug);
51 | this.$scope.global.changeCurrentTab(CONSTANT.TAB_DERIVABLE);
52 | } else if (wordObj.class === CONSTANT.CLASS_GROUP) {
53 | this.$scope.global.currentGroup = wordObj.title.replaceAll(" ", "-");
54 | this.$scope.global.changeCurrentTab(CONSTANT.TAB_GROUP);
55 | /*
56 | Category animation killing in touchEnd:
57 | */
58 | } else if (wordObj.type === "verb") {
59 | this.openVerbConjunction(wordObj);
60 | } else if (wordObj.type === "noun") {
61 | this.openNounConjunction(wordObj);
62 | }
63 | this.clickAnimStop();
64 | }, 300);
65 | this.clickAnimStart(index);
66 | }
67 |
68 | openVerbConjunction(wordObj) {
69 | this.conjArr = [];
70 | this.conj.conjtype.forEach(c => {
71 | let conjable = {
72 | title: this.conj.conjVerb(wordObj.title, c),
73 | tence: c,
74 | };
75 | this.conjArr.push(conjable);
76 | });
77 | document.getElementById("verbConj").style.display = "block";
78 | }
79 |
80 | openNounConjunction(wordObj) {
81 | this.nounConjArr = [];
82 | this.conj.nounCondition.forEach(c => {
83 | let conjable = {
84 | title: this.conj.conjNoun(wordObj.title, c),
85 | condition: c,
86 | };
87 | this.nounConjArr.push(conjable);
88 | });
89 | document.getElementById("nounConj").style.display = "block";
90 | }
91 |
92 | clickConjuncted(wordTitle, tence) {
93 | let wordObj = {};
94 | wordObj.title = wordTitle;
95 | wordObj.slug = this.$scope.global.extendedSlugMap[wordTitle];
96 | wordObj.type = "verb";
97 | wordObj.tence = tence;
98 | this.$scope.global.pushToCurrentPhrase(wordObj, true);
99 | updateCurrentPhraseScroll();
100 | }
101 |
102 | clickNounConjuncted(nounTitle, condition) {
103 | let wordObj = {};
104 | wordObj.title = nounTitle;
105 | wordObj.slug = this.$scope.global.extendedSlugMap[nounTitle];
106 | wordObj.type = "noun";
107 | wordObj.condition = condition;
108 | this.$scope.global.pushToCurrentPhrase(wordObj, true);
109 | updateCurrentPhraseScroll();
110 | }
111 |
112 | closeConjMenu() {
113 | let verbMenu = document.getElementById("verbConj");
114 | if (verbMenu) {
115 | verbMenu.style.display = "none";
116 | }
117 | }
118 |
119 | closeNounConjMenu() {
120 | let nounMenu = document.getElementById("nounConj");
121 | if (nounMenu) {
122 | nounMenu.style.display = "none";
123 | }
124 | }
125 |
126 | /**
127 | * Called when user ends touch event.
128 | * Cancels the onhold actions when touch canceled before 300ms
129 | * @param {Object} wordObj
130 | * @param {number} index
131 | */
132 | wordTouchEnd(wordObj, index) {
133 | this.$timeout.cancel(this.wordTouchTimer);
134 | if (
135 | this.currentlyHolding === wordObj.title &&
136 | wordObj.class !== CONSTANT.CLASS_GROUP
137 | ) {
138 | if (wordObj.type == "verb" || wordObj.type == "noun") {
139 | if (this.wordTouchTimer.$$state.status == 2) {
140 | this.clickWord(wordObj);
141 | }
142 | } else {
143 | this.clickWord(wordObj);
144 | }
145 | }
146 | if (wordObj.class === "group") {
147 | this.$scope.global.currentGroup = wordObj.title.replaceAll(" ", "-");
148 | this.$scope.global.changeCurrentTab(CONSTANT.TAB_GROUP);
149 | }
150 | this.clickAnimEnd(index);
151 | }
152 |
153 | /**
154 | * Starts click animation by adding className to the symbol with given index
155 | * @param {number} index
156 | */
157 | clickAnimStart(index) {
158 | let elem = document.getElementById("item-" + index);
159 | if (elem) {
160 | elem.className = elem.className + " gridItemClick";
161 | }
162 | this.$scope.currentAnimIndex = index;
163 | }
164 |
165 | /**
166 | * Reverses click animation by removing className to the symbol with given index
167 | * @param {number} index
168 | */
169 | clickAnimEnd(index) {
170 | let elem = document.getElementById("item-" + index);
171 | if (elem) {
172 | elem.className = elem.className.replace(" gridItemClick", "");
173 | }
174 | }
175 |
176 | /**
177 | * Cancels the animation for all symbol grids.
178 | * Kills transition by adding a className (transitionKill)
179 | */
180 | clickAnimStop() {
181 | let elem = document.getElementById("item-" + this.$scope.currentAnimIndex);
182 | if (elem) {
183 | elem.className =
184 | elem.className.replace(" gridItemClick", "") + " transitionKill";
185 | this.$timeout(
186 | () => (elem.className = elem.className.replace(" transitionKill", "")),
187 | 101,
188 | );
189 | }
190 | }
191 |
192 | /**
193 | * Pushes the symbol object to current phrase
194 | * Sends word's string to device's TTS Service.
195 | * @param {Object} wordObj
196 | */
197 | clickWord(wordObj) {
198 | this.$scope.global.pushToCurrentPhrase(wordObj, true);
199 | updateCurrentPhraseScroll();
200 | this.events.appWord(
201 | wordObj.title,
202 | this.$scope.global.gridSize[0],
203 | this.$scope.global.gridSize[1],
204 | );
205 | }
206 | }
207 |
208 | // Service Dependency Injection
209 | SymbolController.$inject = [
210 | "$scope",
211 | "$http",
212 | "$timeout",
213 | "$global",
214 | "EventManager",
215 | "ConjunctionManager",
216 | ];
217 |
--------------------------------------------------------------------------------
/app/components/symbol/symbol-test.js:
--------------------------------------------------------------------------------
1 | /* eslint-env jasmine */
2 | import { module, inject } from 'mocks';
3 | import SymbolController from './symbol-controller';
4 | import GridController from '../grid/grid-controller';
5 | import HeaderController from '../header/header-controller';
6 | import * as CONSTANT from '../../js/constants';
7 | import symbolModule from './symbol';
8 | import Global from '../../services/global';
9 |
10 | function randomString(len, charSet) {
11 | charSet = charSet || 'abcdefghijklmnopqrstuvwxyz';
12 | let randomString = '';
13 | for (let i = 0; i < len; i++) {
14 | let randomPoz = Math.floor(Math.random() * charSet.length);
15 | randomString += charSet.substring(randomPoz, randomPoz + 1);
16 | }
17 | return randomString;
18 | }
19 |
20 | function generateSymbol(length, normal, derivable, group) {
21 | let classes = [];
22 | if (normal) { classes.push('normal'); }
23 | if (derivable) { classes.push('derivable'); }
24 | if (group) { classes.push('group'); }
25 | let i = 0;
26 | let symbArray = [];
27 | while (i < length) {
28 | let randString = randomString(7);
29 | let wordObj = {
30 | title: randString,
31 | slug: randString,
32 | type: 'noun',
33 | parent: 'main'
34 | };
35 | wordObj.class = classes[Math.floor(Math.random() * (classes.length - 1)) + 1];
36 | symbArray.push(wordObj);
37 | i++;
38 | }
39 | return symbArray;
40 | }
41 |
42 |
43 | describe('aacApp.symbol module', () => {
44 | beforeEach(module(symbolModule.name));
45 |
46 | let $controller, $timeout;
47 | let symbolCtrl;
48 | let g, tts, cm;
49 | beforeEach(inject((_$controller_, _$timeout_) => {
50 | // The injector unwraps the underscores (_) from around the parameter names when matching
51 | $controller = _$controller_;
52 | $timeout = _$timeout_;
53 | }));
54 |
55 | beforeEach(() => {
56 | g = new Global();
57 | g.changeTab = function() {}
58 | g.currentPhrase = generateSymbol(2, 1, 1, 0);
59 | tts = { speak: function() {} };
60 | event = {
61 | appDerive: function() {},
62 | appWord: function() {}
63 | };
64 | g.pushToCurrentPhrase = function(obj) {
65 | symbolCtrl.$scope.global.currentPhrase.push(obj);
66 | }
67 | cm = {
68 | englishConjunctor: function() {},
69 | turkishConjunctor: function() {},
70 | addPossessiveEn: function() {},
71 | addPossessiveTr: function() {}
72 | };
73 | symbolCtrl = $controller(SymbolController, { $scope: {}, $global: g, $timeout: $timeout, TTSManager: tts, EventManager: event, ConjunctionManager: cm });
74 | $controller(HeaderController, { $scope: {}, $global: g });
75 | });
76 |
77 | describe('symbol controller', () => {
78 | it('should be defined', () => {
79 | expect(symbolCtrl)
80 | .toBeDefined();
81 | });
82 | });
83 |
84 | describe('clickAnimStart', () => {
85 | it('should change the clickAnimIndex', () => {
86 | symbolCtrl.clickAnimStart(5);
87 | expect(symbolCtrl.$scope.currentAnimIndex)
88 | .toBe(5);
89 | });
90 | });
91 |
92 | describe('clickWord', () => {
93 | it('should add the given symbol object to currentPhrase', () => {
94 | let word2add = {
95 | title: 'newWord',
96 | slug: 'newWord'
97 | };
98 | symbolCtrl.clickWord(word2add);
99 | let cp = symbolCtrl.$scope.global.currentPhrase;
100 | expect(JSON.stringify(cp[cp.length - 1]))
101 | .toBe(JSON.stringify(word2add));
102 | });
103 | });
104 |
105 | describe('wordTouchStart', () => {
106 | let dummyWord1 = {
107 | title: 'dummyWord1',
108 | slug: 'dummyWord1',
109 | type: 'naun',
110 | class: 'derive',
111 | parent: 'main',
112 | style: 'gridType-noun'
113 | };
114 | let dummyWord2 = {
115 | title: 'dummyWord2',
116 | slug: 'dummyWord2',
117 | type: 'naun',
118 | class: 'group',
119 | parent: 'main',
120 | style: 'gridType-group'
121 | };
122 | it('should start the when timeout flushed get in derivable page if given symbol is derivable', () => {
123 | symbolCtrl.wordTouchStart(dummyWord1, 0);
124 | $timeout.flush();
125 | expect(symbolCtrl.$scope.global.currentTab)
126 | .toBe(CONSTANT.TAB_MAIN);
127 | expect(symbolCtrl.$scope.currentAnimIndex)
128 | .toBe(0);
129 |
130 | symbolCtrl.wordTouchStart(dummyWord2, 3);
131 | $timeout.flush();
132 | expect(symbolCtrl.$scope.global.currentTab)
133 | .toBe(CONSTANT.TAB_MAIN);
134 | expect(symbolCtrl.$scope.currentAnimIndex)
135 | .toBe(3);
136 |
137 | });
138 |
139 | it('should change currentDerivable to its title if given symbol is derivable', () => {
140 | symbolCtrl.wordTouchStart(dummyWord1, 8);
141 | $timeout.flush();
142 | expect(symbolCtrl.$scope.global.currentDerivable)
143 | .toBe('dummyWord1');
144 | expect(symbolCtrl.$scope.currentAnimIndex)
145 | .toBe(8);
146 | });
147 |
148 | it('should change currentGroup to its title if given symbol is group', () => {
149 | symbolCtrl.wordTouchStart(dummyWord2, 4);
150 | $timeout.flush();
151 | expect(symbolCtrl.$scope.global.currentGroup)
152 | .toBe('dummyWord2');
153 | expect(symbolCtrl.$scope.currentAnimIndex)
154 | .toBe(4);
155 | });
156 | });
157 |
158 |
159 |
160 | describe('wordTouchStart', () => {
161 | let dummyWord1 = {
162 | title: 'dummyWord1',
163 | slug: 'dummyWord1',
164 | type: 'naun',
165 | class: 'derive',
166 | parent: 'main',
167 | style: 'gridType-noun'
168 | };
169 | let dummyWord2 = {
170 | title: 'dummyWord2',
171 | slug: 'dummyWord2',
172 | type: 'naun',
173 | class: 'group',
174 | parent: 'main',
175 | style: 'gridType-group'
176 | };
177 | it('should cancel the wordTouchTimer timeout ', () => {
178 | symbolCtrl.wordTouchStart(dummyWord1, 4);
179 | expect(symbolCtrl.wordTouchTimer.$$state.status)
180 | .toBe(0);
181 | symbolCtrl.wordTouchEnd(dummyWord1, 4);
182 | // Flush timeout if there is any
183 | expect(symbolCtrl.wordTouchTimer.$$state.value)
184 | .toBe('canceled');
185 |
186 | });
187 | it('should add the wordObject to the current phrase if obj is not group', () => {
188 | //Dummuword1 is a derivable class word.
189 | symbolCtrl.wordTouchStart(dummyWord1, 4);
190 | symbolCtrl.wordTouchEnd(dummyWord1, 4);
191 | let cp = symbolCtrl.$scope.global.currentPhrase;
192 | expect(JSON.stringify(cp[cp.length - 1])).toBe(JSON.stringify(dummyWord1));
193 | });
194 | it('should NOT add the wordObject to the current phrase if obj is group', () => {
195 | //Dummuword2 is a group class word.
196 | symbolCtrl.wordTouchStart(dummyWord2, 4);
197 | symbolCtrl.wordTouchEnd(dummyWord2, 4);
198 | let cp = symbolCtrl.$scope.global.currentPhrase;
199 | expect(cp.length).toBe(2);
200 | });
201 | });
202 |
203 | });
204 |
--------------------------------------------------------------------------------
/app/components/symbol/symbol.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
![]()
7 | {{conj.title}}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
![]()
17 | {{conj.title}}
18 |
19 |
20 |
21 |
22 |
23 |
![]()
24 |
{{main.title}}
25 |
{{main.title}}
26 |
27 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/components/symbol/symbol.js:
--------------------------------------------------------------------------------
1 | import angular from 'angular';
2 | import SymbolController from './symbol-controller';
3 |
4 | let symbolModule = angular.module('aacApp.symbol', []);
5 |
6 | symbolModule.directive('symbol', function () {
7 | return {
8 | templateUrl: 'components/symbol/symbol.html'
9 | };
10 | });
11 |
12 | symbolModule.controller('symbol-controller', SymbolController);
13 |
14 | export default symbolModule;
15 |
--------------------------------------------------------------------------------
/app/css/font.css:
--------------------------------------------------------------------------------
1 | /*
2 | - font.css
3 | Used to initilize the font used in app.
4 | Contains latin and latin-extended version of the titillium web open source font.
5 | */
6 |
7 | /* latin-ext */
8 | @font-face {
9 | font-family: 'Titillium Web';
10 | font-style: normal;
11 | font-weight: 400;
12 | src: local('Titillium Web'), local('TitilliumWeb-Regular'), url(../woff/font-latin-ext.woff) format('woff');
13 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
14 | }
15 | /* latin */
16 | @font-face {
17 | font-family: 'Titillium Web';
18 | font-style: normal;
19 | font-weight: 400;
20 | src: local('Titillium Web'), local('TitilliumWeb-Regular'), url(../woff/font.woff) format('woff');
21 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
22 | }
23 |
--------------------------------------------------------------------------------
/app/css/icon.css:
--------------------------------------------------------------------------------
1 | /*
2 | - icon.css
3 | Used to initilizce the Material-icons icon font.
4 | Apache License Version 2.0.
5 | */
6 |
7 | @font-face {
8 | font-family: 'Material Icons';
9 | font-style: normal;
10 | font-weight: 400;
11 | src: local('Material Icons'), local('MaterialIcons-Regular'), url(../woff/icon.woff) format('woff');
12 | }
13 | .material-icons {
14 | font-family: 'Material Icons';
15 | font-weight: normal;
16 | font-style: normal;
17 | font-size: 24px;
18 | line-height: 1;
19 | letter-spacing: normal;
20 | text-transform: none;
21 | display: inline-block;
22 | white-space: nowrap;
23 | word-wrap: normal;
24 | direction: ltr;
25 | text-rendering: optimizeLegibility;
26 | -webkit-font-smoothing: antialiased;
27 | }
--------------------------------------------------------------------------------
/app/css/norm.css:
--------------------------------------------------------------------------------
1 | /*
2 | - norm.css
3 | Used to reset any default behaviour of the environment or browser
4 | that the app is working on.
5 | */
6 |
7 | article, aside, details,
8 | figcaption, figure,
9 | footer, header,
10 | main, menu, nav,
11 | section, summary{display:block;}
12 | audio, canvas, progress, video{display:inline-block;vertical-align:baseline;}
13 | audio:not([controls]){display:none;height:0;}
14 | [hidden], template{display:none;}
15 | a{background-color:transparent;}
16 | a:active, a:hover{outline:0;}
17 | abbr[title]{border-bottom:1px dotted;}
18 | b, strong{font-weight:bold;}
19 | dfn{font-style:italic;}
20 | h1{font-size:2em;margin:0.67em 0;}
21 | mark{background:#ff0;color:#000;}
22 | small{font-size:80%;}
23 | sub, sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}
24 | sup{top:-0.5em;}
25 | sub{bottom:-0.25em;}
26 | img{border:0;}
27 | svg:not(:root){overflow:hidden;}
28 | figure{margin:1em 40px;}
29 | hr{box-sizing:content-box;height:0;}
30 | pre{overflow:auto;}
31 | code, kbd, pre, samp{font-family:monospace, monospace;font-size:1em;}
32 | button, input, optgroup, select, textarea{color:inherit;font:inherit;margin:0;}
33 | button{overflow:visible;}
34 | button, select{text-transform:none;}
35 | button, html input[type="button"],
36 | input[type="reset"], input[type="submit"]{-webkit-appearance:button;cursor:pointer;}
37 | button[disabled], html input[disabled]{cursor:default;}
38 | button::-moz-focus-inner, input::-moz-focus-inner{border:0;padding:0;}
39 | input{line-height:normal;}
40 | input[type="checkbox"],
41 | input[type="radio"]{box-sizing:border-box;padding:0;}
42 | input[type="number"]::-webkit-inner-spin-button,
43 | input[type="number"]::-webkit-outer-spin-button{height:auto;}
44 | input[type="search"]{-webkit-appearance:textfield;}
45 | input[type="search"]::-webkit-search-cancel-button,
46 | input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}
47 | fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em;}
48 | legend{border:0;padding:0;}
49 | textarea{overflow:auto;}
50 | optgroup{font-weight:bold;}
51 | table{border-collapse:collapse;border-spacing:0;}
52 | td, th{padding:0;}
53 | h1, h2, h3,
54 | h4, h5, h6{padding-top:15px;color:#2E2E2E;line-height:1.15em;margin:0 0 0.4em 0;text-rendering:geometricPrecision;}
55 | h1{font-size:2.1rem;letter-spacing:-2px;text-indent:-3px;}
56 | h2{font-size:1.75rem;letter-spacing:-1px;}
57 | h3{font-size:1.5rem;}
58 | h4{font-size:1.3rem;}
59 | h5{font-size:1.2rem;}
60 | h6{font-size:1.1rem;}
61 | a{color:#4A4A4A;transition:color 0.3s ease;text-decoration:none;}
62 | a:hover{color:#111;}
63 | .clear{clear:both;}
64 |
--------------------------------------------------------------------------------
/app/css/sortable.css:
--------------------------------------------------------------------------------
1 | /* ************************************** */
2 | /* Mandatory CSS required for ng-sortable */
3 | /* ************************************** */
4 |
5 | .as-sortable-item, .as-sortable-placeholder {
6 | display: block;
7 | }
8 |
9 | @keyframes mymove {
10 | 0% { transform: rotateZ(-3deg); }
11 | 50% { transform: rotateZ(3deg); }
12 | 100% { transform: rotateZ(-3deg); }
13 | }
14 |
15 | .as-sortable-item {
16 | -ms-touch-action: inital;
17 | touch-action: inital;
18 | /* to disable context menu on iOS devices */
19 | -webkit-touch-callout: none;
20 | }
21 |
22 | .as-sortable-item-handle {
23 | cursor: move;
24 | cursor: -webkit-grab;
25 | cursor: -moz-grab;
26 | }
27 |
28 | .as-sortable-placeholder {
29 | }
30 |
31 | .as-sortable-drag {
32 | position: absolute;
33 | pointer-events: none;
34 | z-index: 9999;
35 | }
36 |
37 | .as-sortable-hidden {
38 | display: none !important;
39 | }
40 |
41 | .as-sortable-un-selectable {
42 | -webkit-touch-callout: none;
43 | -webkit-user-select: none;
44 | -khtml-user-select: none;
45 | -moz-user-select: none;
46 | -ms-user-select: none;
47 | user-select: none;
48 | }
49 |
50 | .as-sortable-item, .as-sortable-placeholder {
51 | float: left;
52 | }
53 |
54 | .as-sortable-item {
55 | transform: rotateZ(3deg) scale(1);
56 | animation: mymove 300ms infinite;
57 | transition:300ms all;
58 | }
59 | .draggable-true .as-sortable-item {
60 | transform: none !important;
61 | animation: none !important;
62 | }
63 |
64 | .as-sortable-item-handle {
65 | }
66 |
67 | .as-sortable-placeholder {
68 | background-color: #eaeaea;
69 | }
70 |
71 | .as-sortable-drag {
72 | opacity: .8;
73 | }
74 |
75 | .as-sortable-dragging .as-sortable-item{
76 | transform: scale(1.2) !important;
77 | animation: none !important;
78 | }
79 |
80 | .as-sortable-hidden {
81 | }
82 |
--------------------------------------------------------------------------------
/app/data/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "settings/gridsize/title": "Picture Grid Size",
3 | "settings/gridsize/description": "The grid size of the picture panel",
4 | "settings/gridsize/keys/grid-3x2/title": "3x2 Size",
5 | "settings/gridsize/keys/grid-3x2/description": "3x2 Size",
6 | "settings/gridsize/keys/grid-3x3/title": "3x3 Size",
7 | "settings/gridsize/keys/grid-3x3/description": "3x3 Size",
8 | "settings/gridsize/keys/grid-4x3/title": "4x3 Size",
9 | "settings/gridsize/keys/grid-4x3/description": "4x3 Size",
10 | "settings/gridsize/keys/grid-5x3/title": "5x3 Size",
11 | "settings/gridsize/keys/grid-5x3/description": "5x3 Size",
12 | "settings/gridsize/keys/grid-5x4/title": "5x4 Size",
13 | "settings/gridsize/keys/grid-5x4/description": "5x4 Size",
14 | "settings/gridsize/keys/grid-6x4/title": "6x4 Size",
15 | "settings/gridsize/keys/grid-6x4/description": "6x4 Size",
16 | "settings/gridsize/keys/grid-6x5/title": "6x5 Size",
17 | "settings/gridsize/keys/grid-6x5/description": "6x5 Size",
18 | "settings/gridsize/keys/grid-8x5/title": "8x5 Size",
19 | "settings/gridsize/keys/grid-8x5/description": "8x5 Size",
20 | "settings/gridsize/keys/grid-10x7/title": "10x7 Size",
21 | "settings/gridsize/keys/grid-10x7/description": "10x7 Size",
22 | "settings/gridsize/keys/grid-11x9/title": "11x9 Size",
23 | "settings/gridsize/keys/grid-11x9/description": "11x9 Size",
24 | "aac_consent": "Send the written words and show analytics.",
25 | "loadingText": "Loading...",
26 | "generalFont": "18px",
27 | "pageText1": "Main Picture Cards",
28 | "pageText2": "Group Cards Of ",
29 | "pageText3": "Derivables Of ",
30 | "pageText4": "Recent Phrases",
31 | "pageText5": "Type With Keyboard",
32 | "startTyping": "Start Typing...",
33 | "timeIntervalText1": "Last 30 Min",
34 | "timeIntervalText2": "Today",
35 | "timeIntervalText3": "Yesterday",
36 | "timeIntervalText4": "Last Week",
37 | "previousText": "Previous",
38 | "nextText": "Next",
39 | "backText": "Go Back",
40 | "completeChangesText": "Complete Changes",
41 | "headerColor": "#3a3e4a",
42 | "removeHoldColor": "#ff0000",
43 | "removeNormalColor": "#444444",
44 | "symbolPack": "symbols/aac-en"
45 | }
46 |
--------------------------------------------------------------------------------
/app/data/test-en.json:
--------------------------------------------------------------------------------
1 | {
2 | "settings/gridsize/title": "Picture Grid Size",
3 | "settings/gridsize/description": "The grid size of the picture panel",
4 | "settings/gridsize/keys/grid-3x2/title": "3x2 Size",
5 | "settings/gridsize/keys/grid-3x2/description": "3x2 Size",
6 | "settings/gridsize/keys/grid-3x3/title": "3x3 Size",
7 | "settings/gridsize/keys/grid-3x3/description": "3x3 Size",
8 | "settings/gridsize/keys/grid-4x3/title": "4x3 Size",
9 | "settings/gridsize/keys/grid-4x3/description": "4x3 Size",
10 | "settings/gridsize/keys/grid-5x3/title": "5x3 Size",
11 | "settings/gridsize/keys/grid-5x3/description": "5x3 Size",
12 | "settings/gridsize/keys/grid-5x4/title": "5x4 Size",
13 | "settings/gridsize/keys/grid-5x4/description": "5x4 Size",
14 | "settings/gridsize/keys/grid-6x4/title": "6x4 Size",
15 | "settings/gridsize/keys/grid-6x4/description": "6x4 Size",
16 | "settings/gridsize/keys/grid-6x5/title": "6x5 Size",
17 | "settings/gridsize/keys/grid-6x5/description": "6x5 Size",
18 | "settings/gridsize/keys/grid-8x5/title": "8x5 Size",
19 | "settings/gridsize/keys/grid-8x5/description": "8x5 Size",
20 | "settings/gridsize/keys/grid-10x7/title": "10x7 Size",
21 | "settings/gridsize/keys/grid-10x7/description": "10x7 Size",
22 | "settings/gridsize/keys/grid-11x9/title": "11x9 Size",
23 | "settings/gridsize/keys/grid-11x9/description": "11x9 Size",
24 | "loadingText": "Loading...",
25 | "generalFont": "18px",
26 | "pageText1": "Main Picture Cards",
27 | "pageText2": "Group Cards Of ",
28 | "pageText3": "Derivables Of ",
29 | "pageText4": "Recent Phrases",
30 | "pageText5": "Type With Keyboard",
31 | "startTyping": "Start Typing...",
32 | "timeIntervalText1": "Last 30 Min",
33 | "timeIntervalText2": "Today",
34 | "timeIntervalText3": "Yesterday",
35 | "timeIntervalText4": "Last Week",
36 | "previousText": "Previous",
37 | "nextText": "Next",
38 | "backText": "Go Back",
39 | "completeChangesText": "Complete Changes",
40 | "headerColor": "#3a3e4a",
41 | "removeHoldColor": "#ff0000",
42 | "removeNormalColor": "#444444",
43 | "symbolPack": "symbols/aac-test-en"
44 | }
45 |
--------------------------------------------------------------------------------
/app/data/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "settings/gridsize/title": "Resim Izgarası Boyutu",
3 | "settings/gridsize/description": "Resim panelinin ızgara boyutu",
4 | "settings/gridsize/keys/grid-3x2/title": "3x2 Boyutu",
5 | "settings/gridsize/keys/grid-3x2/description": "3x2 Boyutu",
6 | "settings/gridsize/keys/grid-3x3/title": "3x3 Boyutu",
7 | "settings/gridsize/keys/grid-3x3/description": "3x3 Boyutu",
8 | "settings/gridsize/keys/grid-4x3/title": "4x3 Boyutu",
9 | "settings/gridsize/keys/grid-4x3/description": "4x3 Boyutu",
10 | "settings/gridsize/keys/grid-5x3/title": "5x3 Boyutu",
11 | "settings/gridsize/keys/grid-5x3/description": "5x3 Boyutu",
12 | "settings/gridsize/keys/grid-5x4/title": "5x4 Boyutu",
13 | "settings/gridsize/keys/grid-5x4/description": "5x4 Boyutu",
14 | "settings/gridsize/keys/grid-6x4/title": "6x4 Boyutu",
15 | "settings/gridsize/keys/grid-6x4/description": "6x4 Boyutu",
16 | "settings/gridsize/keys/grid-6x5/title": "6x5 Boyutu",
17 | "settings/gridsize/keys/grid-6x5/description": "6x5 Boyutu",
18 | "settings/gridsize/keys/grid-8x5/title": "8x5 Boyutu",
19 | "settings/gridsize/keys/grid-8x5/description": "8x5 Boyutu",
20 | "settings/gridsize/keys/grid-10x7/title": "10x7 Boyutu",
21 | "settings/gridsize/keys/grid-10x7/description": "10x7 Boyutu",
22 | "settings/gridsize/keys/grid-11x9/title": "11x9 Boyutu",
23 | "settings/gridsize/keys/grid-11x9/description": "11x9 Boyutu",
24 | "aac_consent": "Yazılan kelimeleri gönder",
25 | "loadingText": "Yükleniyor...",
26 | "generalFont": "18px",
27 | "pageText1": "Tüm Resim Kartları",
28 | "pageText2": "Resim Kartı Grubu: ",
29 | "pageText3": "Türetilmiş Kartlar: ",
30 | "pageText4": "Son Cümleler",
31 | "pageText5": "Klavye ile Yazmak",
32 | "startTyping": "Yazmaya Başla...",
33 | "timeIntervalText1": "Son 30 Dk",
34 | "timeIntervalText2": "Bugün",
35 | "timeIntervalText3": "Dün",
36 | "timeIntervalText4": "Son Hafta",
37 | "previousText": "Önceki",
38 | "nextText": "Sonraki",
39 | "backText": "Geri Dön",
40 | "completeChangesText": "Değişiklikleri Kaydet",
41 | "headerColor": "#3a3e4a",
42 | "removeHoldColor": "#ff0000",
43 | "removeNormalColor": "#444444",
44 | "symbolPack": "symbols/aac-tr"
45 | }
46 |
--------------------------------------------------------------------------------
/app/img/conj-en-noun/from.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
40 |
--------------------------------------------------------------------------------
/app/img/conj-en-noun/in.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/conj-en-noun/my.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
60 |
--------------------------------------------------------------------------------
/app/img/conj-en-noun/simple.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
42 |
--------------------------------------------------------------------------------
/app/img/conj-en-noun/that.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
44 |
--------------------------------------------------------------------------------
/app/img/conj-en-noun/the.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
45 |
--------------------------------------------------------------------------------
/app/img/conj-en-noun/this.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
45 |
--------------------------------------------------------------------------------
/app/img/conj-en-noun/to.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/conj-en-noun/your.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
55 |
--------------------------------------------------------------------------------
/app/img/conj-en/ableMode.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-en/certFutuTence.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-en/imperMode.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-en/ogrGecZam.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-en/possibMode.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-en/presContTence.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-en/simFutuTence.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-en/simPastTence.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-en/simPresTence.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
59 |
--------------------------------------------------------------------------------
/app/img/conj-en/subjunMode.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/benim.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
60 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/bu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
45 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/de.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/den.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
40 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/e.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/i.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
45 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/senin.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
55 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/simple.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
42 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/su.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
44 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/şu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
44 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/şu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
44 |
--------------------------------------------------------------------------------
/app/img/conj-tr-noun/şşşşu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
44 |
--------------------------------------------------------------------------------
/app/img/conj-tr/dilKip.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-tr/emrKip.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-tr/gelZam.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-tr/genZam.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
59 |
--------------------------------------------------------------------------------
/app/img/conj-tr/gerKip.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-tr/gorGecZam.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-tr/istKip.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-tr/ogrGecZam.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/img/conj-tr/simZam.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SPEC App UI
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
76 |
77 |
78 |
79 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/app/js/constants.js:
--------------------------------------------------------------------------------
1 | /*
2 | - constants.js
3 | Holds and exports the constant variables for the whole app to use.
4 | */
5 |
6 | export const CLASS_MAIN = 'main';
7 | export const CLASS_DERIVABLE = 'derive';
8 | export const CLASS_GROUP = 'group';
9 |
10 | export const TAB_MAIN = 'main';
11 | export const TAB_DERIVABLE = 'derivable';
12 | export const TAB_GROUP = 'group';
13 | export const TAB_RECENT = 'recent';
14 | export const TAB_KEYBOARD = 'keyboard';
15 |
16 | export const PORTRAIT = 'portrait';
17 | export const PORTRAIT_PRIMARY = 'portrait-primary';
18 | export const UPSIDE_DOWN = 'upside-down';
19 | export const LANDSCAPE_PRIMARY = 'landscape-primary';
20 | export const LANDSCAPE_LEFT = 'landscape-left';
21 | export const LANDSCAPE_RIGHT = 'landspace-right';
22 |
--------------------------------------------------------------------------------
/app/js/init.js:
--------------------------------------------------------------------------------
1 | if ('addEventListener' in document) {
2 | document.addEventListener('DOMContentLoaded', function() {
3 | FastClick.attach(document.body);
4 | }, false);
5 | }
6 |
7 | if (!localStorage.phraseHistory) {
8 | console.log('LS: not yet initilized, firstTime load.');
9 | localStorage.phraseHistory = '[]';
10 | }
11 |
12 | Array.prototype.contains = function(obj) {
13 | return this.indexOf(obj) > -1;
14 | };
15 |
16 | String.prototype.contains = function(it) {
17 | return this.indexOf(it) != -1;
18 | };
19 |
20 | String.prototype.replaceAll = function(search, replace) {
21 | if (replace === undefined) {
22 | return this.toString();
23 | }
24 |
25 | return this.replace(new RegExp('[' + search + ']', 'g'), replace);
26 | };
27 |
--------------------------------------------------------------------------------
/app/js/utils-test.js:
--------------------------------------------------------------------------------
1 | import * as utils from './utils'
2 |
3 | describe('returnTime', () => {
4 | it('should return an number', () => {
5 | expect(utils.returnTime())
6 | .toBeGreaterThan(0);
7 | expect(utils.returnTime())
8 | .toMatch(/\d{1,}/);
9 | });
10 | });
11 |
12 | describe('capitalize', () => {
13 | it('first letter of the returning string should be capital', () => {
14 | let text = [{ text: 'aqwesafsdg', up: 'A' }, { text: 'ıaşsdiwe', up: 'I' }, { text: '4sdf4sdvs', up: '4' }, { text: 'ğsifşd', up: 'Ğ' }];
15 | text.forEach((t) => {
16 | expect(utils.capitalize(t.text)[0])
17 | .toBe(t.up);
18 | });
19 | });
20 | });
21 |
22 | describe('deviceType', () => {
23 | it('should return the tablet', () => {
24 | let type = utils.deviceType(1000, 600);
25 | expect(type)
26 | .toBe('tablet');
27 | });
28 | it('should return the phone', () => {
29 | let type = utils.deviceType(320, 700);
30 | expect(type)
31 | .toBe('phone');
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/app/js/utils.js:
--------------------------------------------------------------------------------
1 | /*
2 | -- utils.js
3 | This file contains generic functions for other javascript files to use.
4 | Like returnTime(), capitalize() etc.
5 |
6 | */
7 |
8 | /**
9 | * Get unix time
10 | * @returns {number}
11 | */
12 | export function returnTime() {
13 | let d = new Date();
14 | return d.getTime();
15 | }
16 |
17 | /**
18 | * Updates the current phrase's scroll amount
19 | * called after a new word pushed to current phrase
20 | */
21 | export function updateCurrentPhraseScroll() {
22 | let element = document.getElementById("cPhrase");
23 | if (element) {
24 | setTimeout(() => {
25 | element.scrollLeft = element.scrollWidth
26 | }, 1);
27 | }
28 | }
29 |
30 | /**
31 | * capitalize first letter
32 | * @param {string} str
33 | * @returns {string}
34 | */
35 | export function capitalize(str) {
36 | return str.charAt(0).toLocaleUpperCase() + str.slice(1);
37 | }
38 |
39 | /**
40 | * Find the device the types
41 | *
42 | * @param {number} w Width
43 | * @param {number} h Height
44 | * @returns {string} the device types which can be 'phone' or 'tablet'
45 | */
46 | export function deviceType(w, h) {
47 | let type;
48 | let total;
49 | if (w && h) {
50 | total = w + h;
51 | } else {
52 | total = window.innerWidth + window.innerHeight;
53 | }
54 |
55 | if (total < 1500) {
56 | type = "phone";
57 | } else {
58 | type = "tablet";
59 | }
60 | return type;
61 | }
62 |
--------------------------------------------------------------------------------
/app/mocks.js:
--------------------------------------------------------------------------------
1 | if (!window.angular.mock) {
2 | require('angular-mocks')
3 | }
4 | module.exports = window.angular.mock
5 |
--------------------------------------------------------------------------------
/app/otsimo.json:
--------------------------------------------------------------------------------
1 | {
2 | "unique_name": "aac",
3 | "version": "2.4.33",
4 | "main": "index.html",
5 | "licence": "GPL-v3",
6 | "languages": ["en", "tr"],
7 | "supported_orientations": ["all"],
8 | "default_language": "en",
9 | "developer_name": "Otsimo",
10 | "homepage": "https://github.com/otsimo/aac",
11 | "repository": "https://github.com/otsimo/aac.git",
12 | "authors": [
13 | {
14 | "name": "Burak Tokak",
15 | "email": "burak@otsimo.com"
16 | }
17 | ],
18 | "exclude": [],
19 | "settings": "settings.json",
20 | "kv_path": "data",
21 | "capabilities": ["sandbox", "tts"],
22 | "labels": {
23 | "categories": "communication",
24 | "color": "#2ca8c2",
25 | "consent": "aac_consent",
26 | "consent_default": "yes",
27 | "no-game-jump": "yes"
28 | },
29 | "device_filter": {
30 | "edition": {
31 | "exclude": ["kid"]
32 | }
33 | },
34 | "options": {
35 | "gridsize": {
36 | "id": "gridsize",
37 | "type": "string",
38 | "default": "grid-6x4",
39 | "enum": [
40 | "grid-3x2",
41 | "grid-3x3",
42 | "grid-4x3",
43 | "grid-5x3",
44 | "grid-5x4",
45 | "grid-6x4",
46 | "grid-6x5",
47 | "grid-8x5",
48 | "grid-10x7",
49 | "grid-11x9"
50 | ]
51 | }
52 | },
53 | "metadata": [
54 | {
55 | "language": "en",
56 | "visible_name": "Otsimo AAC",
57 | "summary": "Augmentative and alternative way of communication!",
58 | "description": "Otsimo AAC helps children with speech disorders to communicate with significant others by tapping on word cards.",
59 | "keywords": ["aac", "pecs", "phrase", "talking", "communication"],
60 | "logo": "i18n/logo.png",
61 | "icon": "i18n/icon.png",
62 | "images": [
63 | "i18n/en/ss1_en_1024x768.png",
64 | "i18n/en/ss2_en_1024x768.png",
65 | "i18n/en/ss3_en_1024x768.png"
66 | ],
67 | "assets": [],
68 | "annotations": {
69 | "knowledge": "Children gain ability of self-expression by using AAC symbols. By this way they learn to communicate effectively.",
70 | "thinking-skills": "Children can develop language skills with the help of verbal labels of objects and actions.",
71 | "physical-skills": "Children can control their finger movements by pointing and tapping.",
72 | "settings/gridsize/title": "Picture Grid Size",
73 | "settings/gridsize/description": "The grid size of the picture panel",
74 | "settings/gridsize/keys/grid-3x2/title": "3x2 Size",
75 | "settings/gridsize/keys/grid-3x2/description": "3x2 Size",
76 | "settings/gridsize/keys/grid-3x3/title": "3x3 Size",
77 | "settings/gridsize/keys/grid-3x3/description": "3x3 Size",
78 | "settings/gridsize/keys/grid-4x3/title": "4x3 Size",
79 | "settings/gridsize/keys/grid-4x3/description": "4x3 Size",
80 | "settings/gridsize/keys/grid-5x3/title": "5x3 Size",
81 | "settings/gridsize/keys/grid-5x3/description": "5x3 Size",
82 | "settings/gridsize/keys/grid-5x4/title": "5x4 Size",
83 | "settings/gridsize/keys/grid-5x4/description": "5x4 Size",
84 | "settings/gridsize/keys/grid-6x4/title": "6x4 Size",
85 | "settings/gridsize/keys/grid-6x4/description": "6x4 Size",
86 | "settings/gridsize/keys/grid-6x5/title": "6x5 Size",
87 | "settings/gridsize/keys/grid-6x5/description": "6x5 Size",
88 | "settings/gridsize/keys/grid-8x5/title": "8x5 Size",
89 | "settings/gridsize/keys/grid-8x5/description": "8x5 Size",
90 | "settings/gridsize/keys/grid-10x7/title": "10x7 Size",
91 | "settings/gridsize/keys/grid-10x7/description": "10x7 Size",
92 | "settings/gridsize/keys/grid-11x9/title": "11x9 Size",
93 | "settings/gridsize/keys/grid-11x9/description": "11x9 Size"
94 | }
95 | },
96 | {
97 | "language": "tr",
98 | "visible_name": "Otsimo AAC",
99 | "summary": "İletişimin alternatif ve yardımcı yolu!",
100 | "description": "Otsimo AAC konuşma bozukluğu olan çocukların çevresindekilerle iletişim kurmasını sağlamak amacıyla geliştirilen bir uygulamadır. Çocuklar sembollerin üzerine tıklayarak söylemek istediklerini ifade edebilirler.",
101 | "keywords": ["aac", "pecs", "cümle", "konuşma", "iletişim"],
102 | "logo": "i18n/logo.png",
103 | "icon": "i18n/icon.png",
104 | "images": [
105 | "i18n/tr/ss1_tr_1024x768.png",
106 | "i18n/tr/ss2_tr_1024x768.png",
107 | "i18n/tr/ss3_tr_1024x768.png"
108 | ],
109 | "assets": [],
110 | "annotations": {
111 | "knowledge": "Konuşma güçlüğü çeken çocuklar AAC sembollerini kullanarak kendilerini kolayca ifade edebilirler. Bu şekilde aileleriyle etkili bir iletişim kurabilirler.",
112 | "thinking-skills": "Çocuklar sesli semboller yardımıyla objeleri ve faaliyetleri öğrenebilir.",
113 | "physical-skills": "Çocuklar parmak ile işaret etmeyi öğrenirken tıklama hareketleriyle parmak hareketlerini kontrol edebilir.",
114 | "settings/gridsize/title": "Resim Izgarası Boyutu",
115 | "settings/gridsize/description": "Resim panelinin ızgara boyutu",
116 | "settings/gridsize/keys/grid-3x2/title": "3x2 Boyutu",
117 | "settings/gridsize/keys/grid-3x2/description": "3x2 Boyutu",
118 | "settings/gridsize/keys/grid-3x3/title": "3x3 Boyutu",
119 | "settings/gridsize/keys/grid-3x3/description": "3x3 Boyutu",
120 | "settings/gridsize/keys/grid-4x3/title": "4x3 Boyutu",
121 | "settings/gridsize/keys/grid-4x3/description": "4x3 Boyutu",
122 | "settings/gridsize/keys/grid-5x3/title": "5x3 Boyutu",
123 | "settings/gridsize/keys/grid-5x3/description": "5x3 Boyutu",
124 | "settings/gridsize/keys/grid-5x4/title": "5x4 Boyutu",
125 | "settings/gridsize/keys/grid-5x4/description": "5x4 Boyutu",
126 | "settings/gridsize/keys/grid-6x4/title": "6x4 Boyutu",
127 | "settings/gridsize/keys/grid-6x4/description": "6x4 Boyutu",
128 | "settings/gridsize/keys/grid-6x5/title": "6x5 Boyutu",
129 | "settings/gridsize/keys/grid-6x5/description": "6x5 Boyutu",
130 | "settings/gridsize/keys/grid-8x5/title": "8x5 Boyutu",
131 | "settings/gridsize/keys/grid-8x5/description": "8x5 Boyutu",
132 | "settings/gridsize/keys/grid-10x7/title": "10x7 Boyutu",
133 | "settings/gridsize/keys/grid-10x7/description": "10x7 Boyutu",
134 | "settings/gridsize/keys/grid-11x9/title": "11x9 Boyutu",
135 | "settings/gridsize/keys/grid-11x9/description": "11x9 Boyutu"
136 | }
137 | }
138 | ]
139 | }
140 |
--------------------------------------------------------------------------------
/app/services/conjunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ConjunctionManager handles all events
3 | * @export
4 | * @class ConjunctionManager
5 | */
6 |
7 | export default class ConjunctionManager {
8 | constructor() {
9 | const symbolPackPath = otsimo.kv.symbolPack;
10 | const so = symbolPackPath.replace('symbols/', '');
11 | const pluginModule = require(`./../symbols/${so}/main.js`);
12 | this.pm = new pluginModule.default();
13 | this.name = this.pm.name;
14 | this.conjtype = this.pm.conjtype;
15 | this.defaultConjunctor = this.pm.defaultConjunctor;
16 | this.poss = this.pm.poss;
17 | this.nounCondition = this.pm.nounCondition;
18 | }
19 | conjVerb(verb, tence, poss) {
20 | return this.pm.conjVerb(verb, tence, poss);
21 | }
22 | conjNoun(noun, type) {
23 | return this.pm.conjNoun(noun, type);
24 | }
25 | addPoss(verb, poss, tence) {
26 | return this.pm.addPoss(verb, poss, tence);
27 | }
28 | beforePushing(wordObj, currentPhrase) {
29 | return this.pm.beforePushing(wordObj, currentPhrase);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/services/event.js:
--------------------------------------------------------------------------------
1 | /**
2 | * EventManager handles all events
3 | * @export
4 | * @class EventManager
5 | */
6 | export default class EventManager {
7 | /**
8 | * Custom Event: Sends submitted current phrase as string with white spaces (word1 word2 word3 ...).
9 | * @param {string} currentPhrase
10 | */
11 | appPhrase(currentPhrase) {
12 | otsimo.customevent('app:phrase', {phrase: currentPhrase});
13 | }
14 | /**
15 | * Custom Event: Sends the time interval changes in the recent tab
16 | * @param {number} timeInterval
17 | */
18 | appInterval(timeInterval) {
19 | otsimo.customevent('app:time_interval', {recent_time_interval: timeInterval});
20 | }
21 | /**
22 | * Custom Event: Sends the derivable word when user holds on one.
23 | * @param {string} derived
24 | */
25 | appDerive(derived) {
26 | otsimo.customevent('app:derive', {derivative: derived});
27 | }
28 | /**
29 | * Custom Event: Sends a word that added to current pharase list
30 | * @param {string} word the word
31 | * @param {number} x grid height
32 | * @param {number} y grid width
33 | */
34 | appWord(word, x, y, isKeyboard) {
35 | if (isKeyboard && word.includes(" ")) {
36 | //Submitted string is a keyboard input.
37 | word.split(" ").forEach((w) => {
38 | this.appWord(w, x, y, true);
39 | });
40 | } else {
41 | otsimo.customevent('app:word', {
42 | word: word,
43 | grid_x: x,
44 | grid_y: y,
45 | grid_xy: x + 'x' + y,
46 | is_keyboard: isKeyboard
47 | ? true
48 | : false
49 | });
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/services/global.js:
--------------------------------------------------------------------------------
1 | /*
2 | - global.js
3 | A Service for the whole app to share global variables and functions.
4 | */
5 |
6 | export default class Global {
7 | constructor() {
8 | console.log("GLOBAL INITED");
9 | this.currentPhrase = [];
10 | this.mainArray = [];
11 | this.extendedArray = [];
12 | this.extendedTitleArray = [];
13 | this.extendedSlugArray = [];
14 | this.extendedSlugMap = [];
15 | this.phraseIndex = 1;
16 | this.isHome = 1;
17 | this.currentTab = '';
18 | this.currentPage = '';
19 | this.currentDerivable = '';
20 | this.currentGroup = '';
21 | this.gridSize = [0, 0];
22 | this.gridSizeStatic = [0, 0];
23 | this.gridQuantity = 0;
24 | this.isDraggable = true;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/services/localstorage-test.js:
--------------------------------------------------------------------------------
1 | /* eslint-env jasmine */
2 | import { module, inject } from 'mocks'
3 | import LSManager from './localstorage'
4 |
5 | let localstorage = new LSManager();
6 |
7 | describe('getHistoryAsArray', () => {
8 | let word1 = {};
9 | word1.title = 'sample1';
10 | word1.slug = 'sample1';
11 | word1.type = 'naun';
12 | word1.class = 'derive';
13 | word1.parent = 'main';
14 | word1.style = 'gridType-noun';
15 | let word2 = {};
16 | word2.title = 'sample2';
17 | word2.slug = 'sample2';
18 | word2.type = 'naun';
19 | word2.class = 'word';
20 | word2.parent = 'main';
21 | word2.style = 'gridType-noun';
22 | let samplePhrase = [word1, word2];
23 | beforeEach(() => {
24 | localStorage.phraseHistory = '[{"phrase":[{"title":"değil","slug":"değil","type":"noun","class":"word","parent":"main","style":"gridType-noun"},{"title":"kaplumbağa","slug":"kaplumbağa","type":"noun","class":"word","parent":"hayvanlar","style":"gridType-noun"},{"title":"aslan","slug":"aslan","type":"noun","class":"word","parent":"hayvanlar","style":"gridType-noun"},{"title":"koyun","slug":"koyun","type":"noun","class":"word","parent":"hayvanlar","style":"gridType-noun"},{"title":"kelebek","slug":"kelebek","type":"noun","class":"word","parent":"hayvanlar","style":"gridType-noun"}],"time":1473232105695},{"phrase":[{"title":"değil","slug":"değil","type":"noun","class":"word","parent":"main","style":"gridType-noun"},{"title":"babaannem","slug":"babaannem","type":"noun","class":"word","parent":"insanlar","style":"gridType-noun"}],"time":1473232819800},{"phrase":[{"title":"kar","slug":"kar","type":"noun","class":"word","parent":"doğa","style":"gridType-noun"},{"title":"deniz","slug":"deniz","type":"noun","class":"word","parent":"doğa","style":"gridType-noun"}],"time":1473233325367},{"phrase":[{"title":"o","slug":"o","type":"noun","class":"derive","parent":"main","style":"gridType-noun"},{"title":"seviyorum","slug":"seviyorum","type":"noun","class":"derive","parent":"main","style":"gridType-noun"}],"time":1473235018102},{"phrase":[{"title":"evet","slug":"evet","type":"noun","class":"word","parent":"sohbet","style":"gridType-noun"},{"title":"istiyorum","slug":"istiyorum","type":"noun","class":"derive","parent":"main","style":"gridType-noun"},{"title":"değil","slug":"değil","type":"noun","class":"word","parent":"main","style":"gridType-noun"},{"title":"gidiyorum","slug":"gidiyorum","type":"noun","class":"derive","parent":"main","style":"gridType-noun"},{"title":"omuz","slug":"omuz","type":"noun","class":"word","parent":"vücut","style":"gridType-noun"},{"title":"kulak","slug":"kulak","type":"noun","class":"word","parent":"vücut","style":"gridType-noun"}],"time":1473236466056},{"phrase":[{"title":"o","slug":"o","type":"noun","class":"derive","parent":"main","style":"gridType-noun"},{"title":"seviyorum","slug":"seviyorum","type":"noun","class":"derive","parent":"main","style":"gridType-noun"},{"title":"diş macunu","slug":"diş-macunu","type":"noun","class":"word","parent":"eşyalar","style":"gridType-noun"}],"time":1473236841817},{"phrase":[{"title":"iyi geceler","slug":"iyi-geceler","type":"noun","class":"word","parent":"sohbet","style":"gridType-noun"},{"title":"günaydın","slug":"günaydın","type":"noun","class":"word","parent":"sohbet","style":"gridType-noun"}],"time":1473236882704},{"phrase":[{"title":"istiyorum","slug":"istiyorum","type":"noun","class":"derive","parent":"main","style":"gridType-noun"},{"title":"istiyorum","slug":"istiyorum","type":"noun","class":"derive","parent":"main","style":"gridType-noun"},{"title":"değil","slug":"değil","type":"noun","class":"word","parent":"main","style":"gridType-noun"}],"time":1473237347528},{"phrase":[{"title":"kulak","slug":"kulak","type":"noun","class":"word","parent":"vücut","style":"gridType-noun"},{"title":"ayak","slug":"ayak","type":"noun","class":"word","parent":"vücut","style":"gridType-noun"},{"title":"dudak","slug":"dudak","type":"noun","class":"word","parent":"vücut","style":"gridType-noun"},{"title":"kol","slug":"kol","type":"noun","class":"word","parent":"vücut","style":"gridType-noun"}],"time":1473238803888}]';
25 |
26 | });
27 |
28 | it('should return an array', () => {
29 | expect(localstorage.getHistoryAsArray()
30 | .length)
31 | .toBeGreaterThan(0);
32 | });
33 | describe('returned array', () => {
34 | it('should have objects with phrase and time properties', () => {
35 | localstorage.getHistoryAsArray()
36 | .forEach((h) => {
37 | expect(h.phrase)
38 | .toBeDefined();
39 | expect(h.time)
40 | .toBeDefined();
41 | });
42 | });
43 | });
44 |
45 | describe('time property', () => {
46 | it('should be a number', () => {
47 | localstorage.getHistoryAsArray()
48 | .forEach((h) => {
49 | expect(h.time)
50 | .toBeGreaterThan(0);
51 | });
52 | });
53 | });
54 |
55 | describe('phrase property', () => {
56 | it('should be array of symbol objects with atleast 1 object', () => {
57 | localstorage.getHistoryAsArray()
58 | .forEach((h) => {
59 | expect(h.phrase.length)
60 | .toBeGreaterThan(0);
61 | });
62 | });
63 | });
64 |
65 | describe('symbol objects in the phrase', () => {
66 | it('should have title, slug, type, class, parent and style property', () => {
67 |
68 | localstorage.getHistoryAsArray()
69 | .forEach((h) => {
70 | h.phrase.forEach((p) => {
71 | expect(p.title)
72 | .toBeDefined();
73 | expect(p.slug)
74 | .toBeDefined();
75 | expect(p.type)
76 | .toBeDefined();
77 | expect(p.class)
78 | .toBeDefined();
79 | expect(p.parent)
80 | .toBeDefined();
81 | expect(p.style)
82 | .toBeDefined();
83 | });
84 | });
85 | });
86 | });
87 | describe('updateHistoryAsString', () => {
88 | it('should update the localStorage.phraseHistory data to the given string', () => {
89 | localstorage.updateHistoryAsString('asdasdasd');
90 | expect(localStorage.phraseHistory)
91 | .toBe('"asdasdasd"'); // With quotes
92 | });
93 | });
94 |
95 | describe('addPhrase2History', () => {
96 | it('should push a new phrase to the history', () => {
97 | localstorage.addPhrase2History(samplePhrase);
98 | expect(localstorage.getHistoryAsArray()[localstorage.getHistoryAsArray()
99 | .length - 1].phrase.length)
100 | .toBe(2);
101 | expect(localstorage.getHistoryAsArray()[localstorage.getHistoryAsArray()
102 | .length - 1].phrase[0].title)
103 | .toBe('sample1');
104 | expect(localstorage.getHistoryAsArray()[localstorage.getHistoryAsArray()
105 | .length - 1].phrase[1].title)
106 | .toBe('sample2');
107 | expect(localstorage.getHistoryAsArray()[localstorage.getHistoryAsArray()
108 | .length - 1].time)
109 | .toBeGreaterThan(0);
110 | });
111 | it('should not add duplicate phrase entity', () => {
112 | localstorage.addPhrase2History(samplePhrase);
113 | localstorage.addPhrase2History(samplePhrase);
114 | expect(localstorage.getHistoryAsArray()[localstorage.getHistoryAsArray()
115 | .length - 1].phrase)
116 | .not
117 | .toBe(localstorage.getHistoryAsArray()[localstorage.getHistoryAsArray()
118 | .length - 2].phrase);
119 | });
120 | });
121 | });
122 |
--------------------------------------------------------------------------------
/app/services/localstorage.js:
--------------------------------------------------------------------------------
1 | import { returnTime } from '../js/utils'
2 | /**
3 | * LSManager handles actions with localstorage utility
4 | The recent phrase history is stored in the localstorage utility of HTML5
5 | This service contains an interface for modules to interact with localstorage.
6 | * @export
7 | * @class LSManager
8 | */
9 | export default class LSManager {
10 |
11 | /**
12 | * Returns the phrase history as an array
13 | * @returns {array} lastPhrases
14 | */
15 | getHistoryAsArray() {
16 | return JSON.parse(localStorage.phraseHistory);
17 | }
18 |
19 | /**
20 | * Updates the history object with given
21 | * @param {string} json string of the history
22 | */
23 | updateHistoryAsString(tempArr) {
24 | localStorage.phraseHistory = JSON.stringify(tempArr);
25 | }
26 |
27 | /**
28 | * Adds a phrase (array of word objects) to the history
29 | * @param {array} array of word objects
30 | */
31 | addPhrase2History(arrPhrase) {
32 | let tempHistoryArr = this.getHistoryAsArray();
33 | let stringifyLast;
34 | if (tempHistoryArr.length > 0) {
35 | stringifyLast = JSON.stringify(tempHistoryArr[tempHistoryArr.length - 1].phrase);
36 | }
37 | if (stringifyLast !== JSON.stringify(arrPhrase)) {
38 | let obj2Push = {};
39 | obj2Push.phrase = arrPhrase;
40 | obj2Push.time = returnTime();
41 | tempHistoryArr.push(obj2Push);
42 | this.updateHistoryAsString(tempHistoryArr);
43 | }
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/app/services/otsimo-handler.js:
--------------------------------------------------------------------------------
1 | /**
2 | * OtsimoHandler handles otsimo object in the tests
3 | * @export
4 | * @class OtsimoHandler
5 | */
6 | export default class OtsimoHandler {
7 | init() {
8 | return otsimo;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/services/tts.js:
--------------------------------------------------------------------------------
1 | import otsimo from "otsimo";
2 | /**
3 | * TTSManager handles tts actions
4 | *
5 | * @export
6 | * @class TTSManager
7 | */
8 | export default class TTSManager {
9 | setVoiceDriver(id) {
10 | if (!otsimo.isWKWebView && !otsimo.android) {
11 | const vd = new DummmyVoiceDriver();
12 | otsimo.tts.setDriver(vd);
13 | }
14 | }
15 | /**
16 | * speak
17 | * @param {string} text2Speak the text to speak
18 | */
19 | speak(text2Speak) {
20 | otsimo.tts.speak(text2Speak);
21 | }
22 | }
23 |
24 | /**
25 | * Dummy voice driver for development, using responsivevoice.com api.
26 | *
27 | * @export
28 | * @class DummmyVoiceDriver
29 | */
30 | class DummmyVoiceDriver {
31 | speak(text) {
32 | console.log("speak", text);
33 | }
34 |
35 | setVoice(voice) {
36 | console.log("setVoice", voice);
37 | }
38 |
39 | getVoice() {
40 | return "";
41 | }
42 |
43 | voiceList() {
44 | return [];
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/symbols/aac-test-en/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "symbols": [
3 | {
4 | "title": "i",
5 | "slug": "i",
6 | "type": "noun",
7 | "class": "derive",
8 | "parent": "main",
9 | "synonym": []
10 | },
11 | {
12 | "title": "we",
13 | "slug": "we",
14 | "type": "noun",
15 | "class": "word",
16 | "parent": "i",
17 | "synonym": []
18 | },
19 | {
20 | "title": "they",
21 | "slug": "they",
22 | "type": "noun",
23 | "class": "word",
24 | "parent": "i",
25 | "synonym": []
26 | },
27 | {
28 | "title": "it",
29 | "slug": "it",
30 | "type": "noun",
31 | "class": "word",
32 | "parent": "i",
33 | "synonym": []
34 | },
35 | {
36 | "title": "you",
37 | "slug": "you",
38 | "type": "noun",
39 | "class": "derive",
40 | "parent": "main",
41 | "synonym": []
42 | },
43 | {
44 | "title": "it",
45 | "slug": "it",
46 | "type": "noun",
47 | "class": "word",
48 | "parent": "you",
49 | "synonym": []
50 | }
51 | ]
52 | }
53 |
--------------------------------------------------------------------------------
/app/symbols/aac-test-en/images/i.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/symbols/aac-test-en/images/it.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/symbols/aac-test-en/images/they.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/symbols/aac-test-en/images/we.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/symbols/aac-test-en/images/you.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/symbols/aac-test-en/main.js:
--------------------------------------------------------------------------------
1 | export default class LanguagePack {
2 | constructor() {
3 | this.name = "test-en";
4 | this.poss = [""];
5 | this.conjtype = [""];
6 | this.nounCondition = [""];
7 | }
8 |
9 | conjVerb(verb, tence, poss) {
10 | // Verb conjunction function
11 | }
12 |
13 | conjNoun(noun, type) {
14 | // Noun condition function
15 | }
16 | addPoss(verb, poss, tence, identifier) {
17 | // Possessor function
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/symbols/aac-test-en/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": "data.json",
3 | "images": "images",
4 | "language": "en",
5 | "voiceId": "com.apple.ttsbundle.Samantha-compact"
6 | }
--------------------------------------------------------------------------------
/app/symbols/back.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/symbols/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/symbols/prev.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/woff/font-latin-ext.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/app/woff/font-latin-ext.woff
--------------------------------------------------------------------------------
/app/woff/font.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/app/woff/font.woff
--------------------------------------------------------------------------------
/app/woff/icon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/app/woff/icon.woff
--------------------------------------------------------------------------------
/ci/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.11
2 |
3 | ENV OTSIMOCTL_VERSION=v0.17.2
4 | ENV OTSIMO_ROOT_DIR=/opt/otsimo-games
5 |
6 | RUN apk add --update bash ca-certificates curl wget ffmpeg nodejs yarn git python3 py3-setuptools \
7 | && python3 -m ensurepip \
8 | && pip3 install --upgrade awscli s3cmd python-magic \
9 | && rm -rf /var/cache/apk/* \
10 | && wget -O /usr/local/bin/otsimoctl https://s3.eu-central-1.amazonaws.com/repos.otsimo.com/gitlab/15371508/${OTSIMOCTL_VERSION}/otsimoctl-linux-amd64 \
11 | && chmod +x /usr/local/bin/otsimoctl
12 |
13 | ADD start.sh /opt/otsimo-generator/start.sh
14 | WORKDIR /opt/otsimo-games
15 |
16 | ENTRYPOINT ["sh", "/opt/otsimo-generator/start.sh"]
17 |
--------------------------------------------------------------------------------
/ci/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export OTSIMO_ROOT_DIR="${OTSIMO_ROOT_DIR:-/opt/otsimo-games}"
4 |
5 | echo "clone '$OTSIMO_GIT_BRANCH' branch of '$OTSIMO_GIT_REPOSITORY' repository to $OTSIMO_ROOT_DIR"
6 | git clone -b $OTSIMO_GIT_BRANCH $OTSIMO_GIT_REPOSITORY $OTSIMO_ROOT_DIR
7 |
8 | cd $OTSIMO_ROOT_DIR
9 |
10 | echo "install node packages..."
11 | yarn
12 |
13 | echo "start generator..."
14 | yarn run generator
15 |
--------------------------------------------------------------------------------
/e2e-tests/protractor.conf.js:
--------------------------------------------------------------------------------
1 | exports.config = {
2 | allScriptsTimeout: 11000,
3 | specs: [
4 | '*.js'
5 | ],
6 | // capabilities: {
7 | // 'browserName': 'chrome'
8 | // },
9 | baseUrl: 'http://localhost:3000/',
10 | framework: 'jasmine',
11 | jasmineNodeOpts: {
12 | defaultTimeoutInterval: 30000
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/e2e-tests/scenarios.js:
--------------------------------------------------------------------------------
1 | /* eslint-env protractor, jasmine */
2 | 'use strict'
3 |
4 | /* https://github.com/angular/protractor/blob/master/docs/toc.md */
5 |
6 | describe('my app', function () {
7 | it('should automatically redirect to /view1 when location hash/fragment is empty', function () {
8 | browser.get('index.html')
9 | expect(browser.getLocationAbsUrl()).toMatch('/view1')
10 | })
11 |
12 | describe('view1', function () {
13 | beforeEach(function () {
14 | browser.get('index.html#!/view1')
15 | })
16 |
17 | it('should render view1 when user navigates to /view1', function () {
18 | expect(element.all(by.css('ng-view p')).first().getText()).toMatch(/partial for view 1/)
19 | })
20 | })
21 |
22 | describe('view2', function () {
23 | beforeEach(function () {
24 | browser.get('index.html#!/view2')
25 | })
26 |
27 | it('should render view2 when user navigates to /view2', function () {
28 | expect(element.all(by.css('ng-view p')).first().getText()).toMatch(/partial for view 2/)
29 | })
30 | })
31 | })
32 |
--------------------------------------------------------------------------------
/generator.js:
--------------------------------------------------------------------------------
1 | const generator = require("otsimo-game-generator");
2 | const child_process = require("child_process");
3 | const path = require("path");
4 | function execPromise(command) {
5 | return new Promise((resolve, reject) => {
6 | const cmd = child_process.execFile("sh", ["-c", command], {
7 | maxBuffer: 2000 * 1024,
8 | });
9 | cmd.stdout.pipe(process.stdout);
10 | cmd.stderr.pipe(process.stderr);
11 | cmd.on("exit", (code, signal) => {
12 | console.log("child process exited with", code, "and", signal);
13 | if (code == 0) {
14 | resolve(code);
15 | } else {
16 | reject("failed to build");
17 | }
18 | });
19 | });
20 | }
21 | var gopts = [
22 | {
23 | id: "gridsize",
24 | type: "string",
25 | default: "grid-6x4",
26 | enum: [
27 | "grid-3x2",
28 | "grid-3x3",
29 | "grid-4x3",
30 | "grid-5x3",
31 | "grid-5x4",
32 | "grid-6x4",
33 | "grid-6x5",
34 | "grid-8x5",
35 | "grid-10x7",
36 | "grid-11x9",
37 | ],
38 | },
39 | ];
40 |
41 | function optionAnnotations(lang) {
42 | if (lang === "tr") {
43 | return {
44 | "settings/gridsize/title": "Resim Izgarası Boyutu",
45 | "settings/gridsize/description": "Resim panelinin ızgara boyutu",
46 | "settings/gridsize/keys/grid-3x2/title": "3x2 Boyutu",
47 | "settings/gridsize/keys/grid-3x2/description": "3x2 Boyutu",
48 | "settings/gridsize/keys/grid-3x3/title": "3x3 Boyutu",
49 | "settings/gridsize/keys/grid-3x3/description": "3x3 Boyutu",
50 | "settings/gridsize/keys/grid-4x3/title": "4x3 Boyutu",
51 | "settings/gridsize/keys/grid-4x3/description": "4x3 Boyutu",
52 | "settings/gridsize/keys/grid-5x3/title": "5x3 Boyutu",
53 | "settings/gridsize/keys/grid-5x3/description": "5x3 Boyutu",
54 | "settings/gridsize/keys/grid-5x4/title": "5x4 Boyutu",
55 | "settings/gridsize/keys/grid-5x4/description": "5x4 Boyutu",
56 | "settings/gridsize/keys/grid-6x4/title": "6x4 Boyutu",
57 | "settings/gridsize/keys/grid-6x4/description": "6x4 Boyutu",
58 | "settings/gridsize/keys/grid-6x5/title": "6x5 Boyutu",
59 | "settings/gridsize/keys/grid-6x5/description": "6x5 Boyutu",
60 | "settings/gridsize/keys/grid-8x5/title": "8x5 Boyutu",
61 | "settings/gridsize/keys/grid-8x5/description": "8x5 Boyutu",
62 | "settings/gridsize/keys/grid-10x7/title": "10x7 Boyutu",
63 | "settings/gridsize/keys/grid-10x7/description": "10x7 Boyutu",
64 | "settings/gridsize/keys/grid-11x9/title": "11x9 Boyutu",
65 | "settings/gridsize/keys/grid-11x9/description": "11x9 Boyutu",
66 | };
67 | }
68 | return {
69 | "settings/gridsize/title": "Picture Grid Size",
70 | "settings/gridsize/description": "The grid size of the picture panel",
71 | "settings/gridsize/keys/grid-3x2/title": "3x2 Size",
72 | "settings/gridsize/keys/grid-3x2/description": "3x2 Size",
73 | "settings/gridsize/keys/grid-3x3/title": "3x3 Size",
74 | "settings/gridsize/keys/grid-3x3/description": "3x3 Size",
75 | "settings/gridsize/keys/grid-4x3/title": "4x3 Size",
76 | "settings/gridsize/keys/grid-4x3/description": "4x3 Size",
77 | "settings/gridsize/keys/grid-5x3/title": "5x3 Size",
78 | "settings/gridsize/keys/grid-5x3/description": "5x3 Size",
79 | "settings/gridsize/keys/grid-5x4/title": "5x4 Size",
80 | "settings/gridsize/keys/grid-5x4/description": "5x4 Size",
81 | "settings/gridsize/keys/grid-6x4/title": "6x4 Size",
82 | "settings/gridsize/keys/grid-6x4/description": "6x4 Size",
83 | "settings/gridsize/keys/grid-6x5/title": "6x5 Size",
84 | "settings/gridsize/keys/grid-6x5/description": "6x5 Size",
85 | "settings/gridsize/keys/grid-8x5/title": "8x5 Size",
86 | "settings/gridsize/keys/grid-8x5/description": "8x5 Size",
87 | "settings/gridsize/keys/grid-10x7/title": "10x7 Size",
88 | "settings/gridsize/keys/grid-10x7/description": "10x7 Size",
89 | "settings/gridsize/keys/grid-11x9/title": "11x9 Size",
90 | "settings/gridsize/keys/grid-11x9/description": "11x9 Size",
91 | };
92 | }
93 |
94 | const symbolLocation = path.join(__dirname, "app", "symbols");
95 | const getTr = process.env["AAC_TR_REPO"];
96 | const getEn = process.env["AAC_EN_REPO"];
97 |
98 | const command = `
99 | cd ${symbolLocation}
100 | if [ ! -d "aac-tr" ]; then
101 | git clone ${getTr}
102 | fi
103 | if [ ! -d "aac-en" ]; then
104 | git clone ${getEn}
105 | fi
106 | `;
107 |
108 | console.log("build command", command);
109 | execPromise(command)
110 | .then(() => {
111 | generator({
112 | outputDir: "dist",
113 | gameOptions: gopts,
114 | annotations: optionAnnotations,
115 | deviceFilter: {
116 | edition: {
117 | exclude: ["kid"],
118 | },
119 | },
120 | });
121 | })
122 | .catch(err => {
123 | console.error(err);
124 | process.exit(2);
125 | });
126 |
--------------------------------------------------------------------------------
/i18n/en/ss1_en_1024x768.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/i18n/en/ss1_en_1024x768.png
--------------------------------------------------------------------------------
/i18n/en/ss2_en_1024x768.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/i18n/en/ss2_en_1024x768.png
--------------------------------------------------------------------------------
/i18n/en/ss3_en_1024x768.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/i18n/en/ss3_en_1024x768.png
--------------------------------------------------------------------------------
/i18n/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/i18n/icon.png
--------------------------------------------------------------------------------
/i18n/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/i18n/logo.png
--------------------------------------------------------------------------------
/i18n/tr/ss1_tr_1024x768.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/i18n/tr/ss1_tr_1024x768.png
--------------------------------------------------------------------------------
/i18n/tr/ss2_tr_1024x768.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/i18n/tr/ss2_tr_1024x768.png
--------------------------------------------------------------------------------
/i18n/tr/ss3_tr_1024x768.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/otsimo-archive/aac/d213b8123748365ab5fe6ee6469b011ff9ee7e69/i18n/tr/ss3_tr_1024x768.png
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=759670
3 | // for the documentation about the jsconfig.json format
4 | "compilerOptions": {
5 | "target": "es6",
6 | "module": "commonjs",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "exclude": [
10 | "node_modules",
11 | "bower_components",
12 | "jspm_packages",
13 | "tmp",
14 | "temp"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path');
3 | const isTravis = !!process.env.TRAVIS
4 | module.exports = function (config) {
5 | let sourcePath = './app'
6 | let cr = null
7 | let browsers = ['Chrome']
8 | if (isTravis) {
9 | cr = {
10 | type: 'text'
11 | }
12 | browsers = ['Chrome_travis_ci'];
13 | }
14 | config.set({
15 | basePath: '.',
16 | files: [
17 | 'karma.shim.js'
18 | ],
19 | reporters: ['progress', 'coverage'],
20 | preprocessors: {
21 | 'karma.shim.js': ['webpack'],
22 | },
23 | webpack: {
24 | cache: true,
25 | devtool: 'inline-source-map',
26 | entry: {},
27 | output: {},
28 | isparta: {
29 | embedSource: true,
30 | noAutoWrap: true,
31 | // these babel options will be passed only to isparta and not to babel-loader
32 | babel: {
33 | presets: ['es2015']
34 | }
35 | },
36 | module: {
37 | loaders: [
38 | {
39 | test: /\.js$/,
40 | exclude: /(node_modules)/,
41 | loader: 'babel',
42 | query: {
43 | presets: ['es2015']
44 | }
45 | },
46 | // transpile and instrument only testing sources with isparta
47 | {
48 | test: /\.js$/,
49 | include: /^(?!.*\-test.js$)[\/\w\.-]+$/,
50 | exclude: /(node_modules|mocks.js)/,
51 | loader: 'isparta'
52 | }
53 | ]
54 | },
55 | resolve: {
56 | root: [sourcePath],
57 | modules: [
58 | 'node_modules',
59 | sourcePath
60 | ]
61 | }
62 | },
63 | coverageReporter: cr,
64 | autoWatch: true,
65 | frameworks: ['jasmine'],
66 | browsers: browsers,
67 | plugins: [
68 | 'karma-chrome-launcher',
69 | 'karma-firefox-launcher',
70 | 'karma-jasmine',
71 | 'karma-webpack',
72 | 'karma-coverage',
73 | ],
74 | customLaunchers: {
75 | Chrome_travis_ci: {
76 | base: 'Chrome',
77 | flags: ['--no-sandbox']
78 | }
79 | }
80 | });
81 | }
82 |
--------------------------------------------------------------------------------
/karma.shim.js:
--------------------------------------------------------------------------------
1 | // Angular Mocks workaround to the issue
2 | // "angular tried to load more than once".
3 |
4 | 'use strict'
5 |
6 | require('angular')
7 | require('angular-mocks')
8 | let context = require.context('./app', true, /-test$/)
9 | context.keys().forEach(context)
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "otsimo-symbol-aac",
3 | "version": "2.4.32",
4 | "description": "Otsimo symbol based AAC",
5 | "scripts": {
6 | "build": "NODE_ENV=production webpack",
7 | "update-symbols": "node tasks/update-symbols.js",
8 | "staging": "node tasks/version.js;rm -rf ./dist; NODE_ENV=production webpack --progress; otsimoctl -s game publish --release --dir ./dist --aws-sync",
9 | "production": "node tasks/version.js;rm -rf ./dist; NODE_ENV=production webpack --progress; otsimoctl game publish --release --dir ./dist --aws-sync",
10 | "watch": "yarn run build -- --watch",
11 | "server": "webpack-dev-server --port 3001 --host 0.0.0.0",
12 | "start": "yarn run server",
13 | "test": "karma start karma.conf.js",
14 | "test-travis": "karma start karma.conf.js --single-run",
15 | "lint": "eslint app",
16 | "update-webdriver": "webdriver-manager update",
17 | "preprotractor": "yarn run update-webdriver",
18 | "protractor": "protractor e2e-tests/protractor.conf.js",
19 | "generator": "node generator.js"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/otsimo/acc.git"
24 | },
25 | "keywords": [
26 | "otsimo",
27 | "aac"
28 | ],
29 | "author": "Burak Tokak ",
30 | "license": "GPL-3.0",
31 | "bugs": {
32 | "url": "https://github.com/otsimo/acc/issues"
33 | },
34 | "homepage": "https://github.com/otsimo/acc/#readme",
35 | "standard": {
36 | "parser": "babel-eslint"
37 | },
38 | "devDependencies": {
39 | "angular-mocks": "^1.5.7",
40 | "babel-core": "^6.11.4",
41 | "babel-eslint": "^6.1.2",
42 | "babel-loader": "^6.2.4",
43 | "babel-preset-es2015": "^6.9.0",
44 | "copy-webpack-plugin": "^3.0.1",
45 | "eslint": "^3.5.0",
46 | "eslint-config-airbnb": "^11.1.0",
47 | "eslint-plugin-import": "^1.15.0",
48 | "eslint-plugin-jsx-a11y": "^2.2.2",
49 | "eslint-plugin-react": "^6.3.0",
50 | "fs": "0.0.1-security",
51 | "isparta-loader": "^2.0.0",
52 | "jasmine-core": "^2.4.1",
53 | "jasmine-jquery": "^2.1.1",
54 | "karma": "^1.1.1",
55 | "karma-chrome-launcher": "^1.0.1",
56 | "karma-coverage": "^1.1.1",
57 | "karma-firefox-launcher": "^1.0.0",
58 | "karma-jasmine": "^1.0.2",
59 | "karma-jasmine-jquery": "^0.1.1",
60 | "karma-json-fixtures-preprocessor": "0.0.6",
61 | "karma-read-json": "^1.1.0",
62 | "karma-webpack": "^1.7.0",
63 | "ng-annotate-webpack-plugin": "^0.1.3",
64 | "protractor": "^4.0.0",
65 | "standard": "^7.1.2",
66 | "svgo": "^0.7.1",
67 | "webpack": "2.1.0-beta.19",
68 | "webpack-dev-server": "2.1.0-beta.0",
69 | "webpack-uglify-js-plugin": "^1.1.9"
70 | },
71 | "dependencies": {
72 | "angular": "^1.6.0",
73 | "angular-touch": "^1.5.8",
74 | "async": "^2.6.1",
75 | "fastclick": "^1.0.6",
76 | "ng-sortable": "^1.3.6",
77 | "ngtouch": "^1.0.1",
78 | "otsimo": "2.6.190909",
79 | "otsimo-game-generator": "^1.1.3"
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/tasks/update-symbols.js:
--------------------------------------------------------------------------------
1 | var exec = require("child_process").exec;
2 |
3 | const symbolPath = "app/symbols";
4 | // relative to /tasks dir
5 |
6 | const gitServer = "git@gitlab.com:otsimo/aac-";
7 | // git host, which will need authentication.
8 | var langArr = ["en", "tr"];
9 | langArr.forEach(function(lan) {
10 | exec("rm -rf aac-" + symbolPath + "/" + lan + "/", function(err, stdo, stde) {
11 | console.log("Old files removed!");
12 | console.log("Start Cloning..");
13 | exec("git clone " + gitServer + lan + ".git", { cwd: symbolPath }, function(
14 | err,
15 | stdo,
16 | stde,
17 | ) {
18 | console.log(stdo);
19 | console.log(lan + " Loaded!!");
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/tasks/version.js:
--------------------------------------------------------------------------------
1 | /*
2 | a nodejs script to update the otsimo game
3 | manifest version.
4 | */
5 |
6 | var fs = require('fs');
7 |
8 | var directory = "app/otsimo.json";
9 | var file = JSON.parse(fs.readFileSync(directory));
10 | var version = file['version'];
11 | var newVersion = version.split(".")[0] + "." + version.split(".")[1] + "." + parseInt(parseInt(version.split(".")[2]) + 1);
12 |
13 | file['version'] = newVersion;
14 | var fileJSON = JSON.stringify(file, null, 4);
15 | console.log("The version updated to: " + newVersion);
16 | fs.writeFileSync(directory, fileJSON);
17 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const CopyWebpackPlugin = require('copy-webpack-plugin')
4 | const webpackUglifyJsPlugin = require('webpack-uglify-js-plugin');
5 | const webpackDevConfig = require('./webpack.dev.config.js');
6 | const webpackProdConfig = require('./webpack.prod.config.js');
7 |
8 | let outputPath = path.resolve(__dirname, 'dist')
9 | let sourcePath = path.resolve(__dirname, 'app')
10 | let fastClickPath = path.resolve(__dirname, 'node_modules', 'fastclick', 'lib')
11 |
12 | let isProduction = process.env.NODE_ENV === 'production'
13 |
14 | if (isProduction) {
15 | module.exports = webpackProdConfig;
16 | } else {
17 | module.exports = webpackDevConfig;
18 | }
19 |
--------------------------------------------------------------------------------
/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 | const CopyWebpackPlugin = require("copy-webpack-plugin");
4 | const webpackUglifyJsPlugin = require("webpack-uglify-js-plugin");
5 |
6 | let outputPath = path.resolve(__dirname, "dist");
7 | let sourcePath = path.resolve(__dirname, "app");
8 | let otsimoPath = path.resolve(__dirname, "node_modules", "otsimo");
9 | let initPath = path.resolve(__dirname, "app", "js");
10 | let fastClickPath = path.resolve(__dirname, "node_modules", "fastclick", "lib");
11 | let responsivevoicePath = path.resolve(__dirname, "app", "js");
12 |
13 | module.exports = {
14 | devtool: "source-map",
15 | cache: true,
16 | entry: {
17 | vendor: ["angular", "angular-mocks", "ngtouch"],
18 | app: path.join(sourcePath, "app.js"),
19 | },
20 | output: {
21 | path: outputPath,
22 | filename: "[name].js",
23 | sourceMapFilename: "[name].map",
24 | chunkFilename: "[id].chunk.js",
25 | },
26 | module: {
27 | loaders: [
28 | {
29 | test: /\.js$/,
30 | exclude: /(node_modules)/,
31 | loader: "babel",
32 | query: {
33 | presets: ["es2015"],
34 | },
35 | },
36 | ],
37 | },
38 | resolve: {
39 | root: [sourcePath],
40 | modules: ["node_modules", sourcePath],
41 | },
42 | devServer: {
43 | progress: true,
44 | contentBase: outputPath,
45 | outputPath: outputPath,
46 | },
47 | plugins: [
48 | new webpack.optimize.CommonsChunkPlugin({
49 | name: ["app", "vendor"],
50 | minChunks: Infinity,
51 | }),
52 | new CopyWebpackPlugin([
53 | {
54 | context: sourcePath,
55 | from: "**/*.{html,css,woff,json,svg}",
56 | },
57 | {
58 | context: initPath,
59 | from: "init.js",
60 | },
61 | {
62 | context: fastClickPath,
63 | from: "fastclick.js",
64 | },
65 | {
66 | context: responsivevoicePath,
67 | from: "responsivevoice.js",
68 | },
69 | ]),
70 | new webpack.NoErrorsPlugin(),
71 | ],
72 | };
73 |
--------------------------------------------------------------------------------
/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 | const CopyWebpackPlugin = require("copy-webpack-plugin");
4 | const webpackUglifyJsPlugin = require("webpack-uglify-js-plugin");
5 |
6 | let outputPath = path.resolve(__dirname, "dist");
7 | let sourcePath = path.resolve(__dirname, "app");
8 | let initPath = path.resolve(__dirname, "app", "js");
9 | let fastClickPath = path.resolve(__dirname, "node_modules", "fastclick", "lib");
10 |
11 | module.exports = {
12 | devtool: null,
13 | cache: true,
14 | entry: {
15 | vendor: ["angular", "angular-mocks", "ngtouch"],
16 | app: path.join(sourcePath, "app.js"),
17 | },
18 | output: {
19 | path: outputPath,
20 | filename: "[name].js",
21 | sourceMapFilename: "[name].map",
22 | chunkFilename: "[id].chunk.js",
23 | },
24 | module: {
25 | loaders: [
26 | {
27 | test: /\.js$/,
28 | exclude: /(node_modules)/,
29 | loader: "babel",
30 | query: {
31 | presets: ["es2015"],
32 | },
33 | },
34 | ],
35 | },
36 | resolve: {
37 | root: [sourcePath],
38 | modules: ["node_modules", sourcePath],
39 | },
40 | plugins: [
41 | new webpack.optimize.CommonsChunkPlugin({
42 | name: ["app", "vendor"],
43 | minChunks: Infinity,
44 | }),
45 | new webpackUglifyJsPlugin({
46 | cacheFolder: path.resolve(__dirname, "public/cached_uglify/"),
47 | debug: true,
48 | minimize: true,
49 | sourceMap: false,
50 | output: {
51 | comments: true,
52 | },
53 | compressor: {
54 | warnings: false,
55 | },
56 | }),
57 | new CopyWebpackPlugin([
58 | {
59 | context: sourcePath,
60 | from: "**/*.{html,css,woff,json,svg}",
61 | ignore: ["symbols/test-en/**/*.{html,css,woff,json,svg}"],
62 | },
63 | {
64 | context: initPath,
65 | from: "init.js",
66 | },
67 | {
68 | from: "i18n",
69 | to: "i18n",
70 | },
71 | {
72 | context: fastClickPath,
73 | from: "fastclick.js",
74 | },
75 | ]),
76 | new webpack.NoErrorsPlugin(),
77 | ],
78 | };
79 |
--------------------------------------------------------------------------------