added 3 widgets

preferencesAboutTextFull
Clément Fauconnier 7 years ago
parent 24b9d7f15b
commit 071588e288
  1. 1
      resources/library/applications/Calculator.wgt/Calculator.wgt/.gitignore
  2. 33
      resources/library/applications/Calculator.wgt/Calculator.wgt/Gruntfile.js
  3. 14
      resources/library/applications/Calculator.wgt/Calculator.wgt/config.xml
  4. 380
      resources/library/applications/Calculator.wgt/Calculator.wgt/css/calculator.css
  5. 2
      resources/library/applications/Calculator.wgt/Calculator.wgt/dist/calculator.js
  6. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/icon.png
  7. 55
      resources/library/applications/Calculator.wgt/Calculator.wgt/index.html
  8. 22
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/sankore.js
  9. 35
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Button.js
  10. 528
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Calculator.js
  11. 155
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Calculus/Engine.js
  12. 217
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Calculus/Expression.js
  13. 52
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Command.js
  14. 341
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Editor/Editor.js
  15. 44
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Editor/Layout.js
  16. 109
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/KeystrokeLine.js
  17. 23
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Text.js
  18. 323
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/UI/EditorInterface.js
  19. 534
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/UI/MainInterface.js
  20. 15
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Util/Error.js
  21. 48
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Util/EventDispatcher.js
  22. 78
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Util/Hash.js
  23. 45
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Util/I18N.js
  24. 74
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Util/i18n/messages.en.js
  25. 74
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/Util/i18n/messages.fr.js
  26. 78
      resources/library/applications/Calculator.wgt/Calculator.wgt/js/src/Sankore/klass.js
  27. 102
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/css/ubwidget.css
  28. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/arrows_out/bottom.png
  29. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/arrows_out/left.png
  30. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/arrows_out/right.png
  31. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/arrows_out/top.png
  32. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/arrows_over/button_arrow_bottom.png
  33. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/arrows_over/button_arrow_left.png
  34. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/arrows_over/button_arrow_right.png
  35. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/arrows_over/button_arrow_top.png
  36. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/back.png
  37. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/back_small.png
  38. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/button_out-copie.png
  39. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/button_out.gif
  40. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/button_out.png
  41. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/button_out_dark.gif
  42. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/button_out_dark.png
  43. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/button_over.gif
  44. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/button_toggle.png
  45. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/button_toggle_invert.png
  46. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/buttons_shadow/back.png
  47. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/buttons_shadow/bottom.png
  48. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/buttons_shadow/cbottomleft.png
  49. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/buttons_shadow/cbottomright.png
  50. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/buttons_shadow/ctopleft.png
  51. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/buttons_shadow/ctopright.png
  52. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/buttons_shadow/left.png
  53. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/buttons_shadow/right.png
  54. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/buttons_shadow/top.png
  55. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/calculator/pi.png
  56. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/calculator/pi.psd
  57. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/calculator/pi_click.png
  58. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/calculator/pi_over.png
  59. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/calculator/pow.png
  60. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/calculator/pow.psd
  61. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/calculator/sq.png
  62. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/calculator/sq.psd
  63. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/calculator/sq_click.png
  64. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/calculator/sq_over.png
  65. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/display copy.png
  66. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/display.png
  67. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/historyback.png
  68. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/historytab.png
  69. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/historytabOver.png
  70. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/inspector.png
  71. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche0.png
  72. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche0_over.png
  73. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche0_over_down.png
  74. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche1.png
  75. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche1_over.png
  76. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche1_over_down.png
  77. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche2.png
  78. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche2_over.png
  79. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche2_over_down.png
  80. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche3.png
  81. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche3_over.png
  82. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche3_over_down.png
  83. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche4.png
  84. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche4_over.png
  85. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche4_over_down.png
  86. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche5.png
  87. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche5_over.png
  88. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche5_over_down.png
  89. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche6.png
  90. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche6_over.png
  91. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche6_over_down.png
  92. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche7.png
  93. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche7_over.png
  94. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche7_over_down.png
  95. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche8.png
  96. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche8_over.png
  97. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche8_over_down.png
  98. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche9.png
  99. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche9_over.png
  100. BIN
      resources/library/applications/Calculator.wgt/Calculator.wgt/legacy/images/touche9_over_down.png
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,33 @@
module.exports = function (grunt) {
var baseSrc = 'js/src';
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.initConfig({
uglify: {
options: {
separator: ';'
},
compile: {
src: [
baseSrc + '/Sankore/klass.js',
baseSrc + '/**/*.js'
],
dest: 'dist/calculator.js'
}
},
watch: {
scripts: {
files: 'js/src/**/*.js',
tasks: ['scripts:dist']
}
}
});
grunt.registerTask('default', ['dist', 'watch']);
grunt.registerTask('dist', ['scripts:dist']);
grunt.registerTask('scripts:dist', ['uglify:compile']);
};

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns="http://www.w3.org/ns/widgets"
xmlns:ub="http://uniboard.mnemis.com/widgets"
id="http://uniboard.mnemis.com/widgets/calculator"
version="1.1"
width="460"
height="418"
minimum_width="460"
minimum_height="418"
ub:resizable="true"
ub:transparent="true">
<name>Calculator</name>
<content src="index.html"/>
</widget>

@ -0,0 +1,380 @@
* {
margin: 0;
padding: 0;
}
body {
font-size: 12px;
margin:0;
}
.calculator {
-webkit-user-select: none;
font-family: Verdana;
background: -webkit-linear-gradient(top, #f6f6f6 0%, #f7f7f7 25%, #f4f4f4 25%, #e8e8e8 100%);
background: linear-gradient(to bottom, #f6f6f6 0%, #f7f7f7 25%, #f4f4f4 25%, #e8e8e8 100%);
padding: 1em;
width: auto;
border-radius: 0.66em;
border: 1px solid #bbb;
margin: 0.33em;
box-shadow: 0.16em 0.16em 0.46em rgba(0, 0, 0, 0.35), inset 0 0 0.45em rgba(0, 0, 0, 0.3);
}
.calculator .title {
font-weight:bold;
color:#333;
text-shadow:0 1px 1px rgba(0, 0, 0, 0.2);
display:block;
margin-top: -1.1em;
margin-right:25%;
top: 0.4em;
position:relative;
}
.calculator .controls {
float: right;
margin: -1.3em 0.2em 0.25em 0;
}
.calculator .controls button {
background: -webkit-linear-gradient(top, #666666 0%, #444444 100%);
background: linear-gradient(to bottom, #666666 0%, #444444 100%);
padding: 0.12em 0.5em;
border: none;
color: white;
outline: none;
text-shadow: 0 -1px 0 black;
border-radius: 0 0 0.25em 0.25em;
box-shadow: inset 0 -1px 1px #000000, 0 1px 1px rgba(0, 0, 0, 0.5);
}
.calculator .controls button + button {
margin-left:2px;
}
.calculator .controls button:active {
background: -webkit-linear-gradient(bottom, #666666 0%, #444444 100%);
background: linear-gradient(to top, #666666 0%, #444444 100%);
box-shadow: inset 0 -1px 3px #000000;
color: #cccccc;
}
.calculator table {
table-layout: fixed;
line-height:90%;
width: 100%;
border-collapse: separate;
border-spacing: 1px;
}
.calculator .screen td {
height: 1px;
}
.calculator .screen ul {
height: 100%;
cursor: default;
box-sizing: border-box;
margin-bottom: 0.8rem;
padding: 0.4rem;
font-size: 1.5em;
line-height: normal;
text-shadow: 0 1px 1px #ffffff;
color: #444f53;
background: -webkit-linear-gradient(top, #f6f8f9 0%, #d2edf2 70%, #c9e3e7 70%, #d4eff4 100%);
background: linear-gradient(to bottom, #f6f8f9 0%, #d2edf2 70%, #c9e3e7 70%, #d4eff4 100%);
border: 1px solid #d3d3d3;
border-top-color: #d0d0d0;
border-left-color: #d0d0d0;
border-radius: 0.33rem;
box-shadow: 0 0 0.4rem #ffffff, inset 0.15rem 0.15rem 0.4rem rgba(21, 39, 54, 0.5);
}
.calculator .screen li {
list-style-type: none;
}
.calculator .screen .expression-row .caret {
border-left: 1px solid #444f53;
box-sizing: border-box;
margin-right: -1px;
-webkit-animation-name: blinker;
-webkit-animation-duration: 1.2s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: infinite;
animation-name: blinker;
animation-duration: 1.2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
@-webkit-keyframes blinker {
0% {
opacity: 1.0;
}
40% {
opacity: 1.0;
}
50% {
opacity: 0.0;
}
90% {
opacity: 0.0;
}
}
@keyframes blinker {
0% {
opacity: 1.0;
}
40% {
opacity: 1.0;
}
50% {
opacity: 0.0;
}
90% {
opacity: 0.0;
}
}
.calculator .screen .flag-row {
font-size: 0.6em;
min-height: 15px;
overflow: hidden;
}
.calculator .screen .flag-row span {
margin-right:0.3em;
display: inline-block;
}
.calculator .screen .result-row {
min-height: 38px;
font-size: 1.6em;
text-align: right;
}
.calculator .screen .result-row .error {
color: #4b2525;
}
.calculator .screen .euclidean {
font-size: 0.8em;
}
.calculator .screen .euclidean span {
border-bottom: 1px solid #444f53;
position: relative;
margin-bottom: 0.5rem;
display: block;
float: right;
}
.calculator .screen .euclidean .remainder {
margin-left: 0.5em;
}
.calculator .screen .euclidean span:before {
font-size: 0.4em;
display: block;
position: absolute;
bottom: -1em;
width: 100%;
text-align: center;
}
.calculator .screen .euclidean .quotient:before {
content: 'q';
}
.calculator .screen .euclidean .remainder:before {
content: 'r';
}
.calculator .screen .front-screen ul {
overflow: hidden;
}
.calculator .screen .front-screen .expression-row {
min-height: 22px;
max-height: 22px;
white-space: nowrap;
}
.calculator .screen .rear-screen {
width: 50%;
}
.calculator .screen .rear-screen ul {
-webkit-user-select: initial;
height: 103.5%;
margin-right: 0.8rem;
margin-bottom: 0;
font-size: 1.3em;
background: -webkit-linear-gradient(top, #3b474f 0%, #131f21 51%, #0d1516 100%);
background: linear-gradient(to bottom, #3b474f 0%, #131f21 51%, #0d1516 100%);
box-shadow: 0 0 0.4rem #ffffff, inset 0.15rem 0.15rem 0.4rem rgba(21, 39, 54, 0.8);
text-shadow: 0 -1px 1px #000000;
color: #cbe7f4;
overflow-y: scroll;
}
.calculator .screen .rear-screen .expression-row {
word-wrap:break-word;
}
.calculator .screen .rear-screen .euclidian span:before {
font-size: 0.6em;
}
.calculator .edit-area {
height: 100%;
margin-right: 0.8rem;
margin-bottom: 0;
font-size: 1em;
position: relative;
}
.calculator .edit-area hr {
margin: 0.7em 0 0.5em 0;
border: none;
border-top: 1px solid #d0d0d0;
border-bottom: 1px solid #fcfcfc;
}
.calculator .edit-area select {
width: 100%;
}
.calculator .edit-area button.small {
float: right;
width: 9.13%;
height:20px;
padding: 0;
font-size:0;
position:relative;
}
.calculator .edit-area button.small:before {
position: absolute;
display: block;
height:100%;
width: 100%;
font-size:16px;
top: 0;
left: 0;
font-weight:bold;
}
.calculator .edit-area button.small.add:before {
content: '+';
}
.calculator .edit-area button.small.remove:before {
content: '-';
}
.calculator .edit-area button.small + button.small {
margin-right:0.4rem;
}
.calculator .edit-area select.layout-select {
width: 74.77%;
}
.calculator .edit-area label {
width: 100%;
display: block;
color: #555555;
margin: 0.7em 0 0.5em 0;
}
.calculator .edit-area input[type=text],
.calculator .edit-area textarea {
display: block;
box-sizing: border-box;
width: 100%;
border: 1px solid #d0d0d0;
font-size: 1em;
font-family: Verdana;
border-radius: 3px;
padding: 4px 2px;
box-shadow: inset 0.1em 0.1em 0.2em rgba(0, 0, 0, 0.2);
resize: none;
}
.calculator .edit-area input[type=text][disabled],
.calculator .edit-area textarea[disabled] {
background-color: #eeeeee;
color: #555555;
}
.calculator .edit-area .assignation {
margin-top: 1em;
border: 1px solid #d0d0d0;
padding: 0.5em;
box-shadow: inset 0 0 0.5em rgba(255, 255, 255, 1);
border-radius: 3px;
}
.calculator .edit-area .assignation em {
font-size: 1.1em;
line-height: 1.1em;
color: #555555;
text-align: center;
display: block;
}
.calculator .edit-area .assignation label:first-child {
margin-top: 0;
}
.calculator .edit-area .assignation .help {
display: block;
margin-top: 0.3em;
font-size: 0.9em;
color: #777777;
}
.calculator .edit-area .run {
display: block;
width: 100%;
position: absolute;
padding: 0.5em 0;
bottom: 0;
font-weight: bold;
outline: 0;
}
.calculator .buttons td {
padding: 0;
margin: 0;
}
.calculator .buttons button {
outline: 0;
font-family: Verdana;
font-size: 1.2em;
font-weight: 500;
color: #555555;
background: -webkit-linear-gradient(bottom, #eeeeee 0%, #fdfdfd 100%);
background: linear-gradient(to top, #eeeeee 0%, #fdfdfd 100%);
border: 0.4em solid #ffffff;
border-top-width: 0.3em;
border-bottom-width: 0.5em;
border-bottom-color: #eeeeee;
border-right-color: #eeeeee;
border-radius: 0.33rem;
box-shadow: 0.2em 0.2em 0.2em 0.1em rgba(0, 0, 0, 0.2);
box-sizing: border-box;
display: block;
width: 100%;
height: 2.4rem;
padding: 0;
margin: 0;
}
.calculator .buttons button:hover {
color: #29a0b5;
background: -webkit-linear-gradient(bottom, #d0ecf0 0%, #ffffff 100%);
background: linear-gradient(to top, #d0ecf0 0%, #ffffff 100%);
border-bottom-color: #d0ecf0;
border-right-color: #d0ecf0;
}
.calculator .buttons button:active {
font-size: 1.1em;
color: #29a0b5;
border: 1px solid #cccccc;
background: -webkit-linear-gradient(bottom, #d0ecf0 0%, #ffffff 100%);
background: linear-gradient(to top, #d0ecf0 0%, #ffffff 100%);
box-shadow: inset 0 0 0.5em 0.2em rgba(0, 0, 0, 0.1), 0.1em 0.1em 0.2em #ffffff;
}
.calculator .buttons .alt button {
color: #2b8eac;
font-weight: bold;
}
.calculator .buttons .edit button {
border:1px solid #aac3b2;
box-shadow: inset 0 0 1.5em 0.2em rgba(48, 114, 71, 0.3);
color: #678d74;
}
.calculator .buttons .danger button {
font-weight: bold;
}
.calculator .buttons .danger button:hover {
color: #cc5d54;
background: -webkit-linear-gradient(bottom, #f0dad0 0%, #ffffff 100%);
background: linear-gradient(to top, #f0dad0 0%, #ffffff 100%);
border-bottom-color: #f0dad0;
border-right-color: #f0dad0;
}
.calculator .buttons .danger button:active {
color: #cc5d54;
background: -webkit-linear-gradient(bottom, #f0dad0 0%, #ffffff 100%);
background: linear-gradient(to top, #f0dad0 0%, #ffffff 100%);
}
.calculator .buttons button[disabled] {
background: inherit;
box-shadow: 0.1em 0.1em 0.2em #ffffff;
border: 1px solid #cccccc;
color: #cccccc;
}
.calculator .buttons button[disabled]:hover {
background: inherit;
box-shadow: 0.1em 0.1em 0.2em #ffffff;
}
.calculator .buttons button[disabled]:active {
font-size: 1.2em;
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Unpredictable Calculator</title>
<script src="js/sankore.js" type="text/javascript"></script>
<script src="dist/calculator.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="css/calculator.css" />
</head>
<body>
<div id="ubwidget"></div>
<script type="text/javascript">
var unpredictable = false;
var c = Sankore.Calculator.create('ubwidget', {
locale: window.sankore ? window.sankore.locale() : 'fr_FR',
unpredictableMode: unpredictable,
ready: function () {
var self = this, state = {}, timer = null;
if (window.sankore) {
try {
state = JSON.parse(window.sankore.preference('state'));
} catch (e) {}
this.eventDispatcher.addEventSubscriber({
events: [
'calculator.output_changed', 'calculator.memory_changed', 'calculator.layout_changed',
'editor.layout_created', 'editor.layout_removed', 'editor.layout_changed', 'editor.button_selected',
'keystroke_line.changed'
],
listener: function () {
if (null !== timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
window.sankore.setPreference('state', JSON.stringify(self.getState()));
timer = null;
}, 350);
}
});
}
this.init(state);
}
});
</script>
</body>
</html>

@ -0,0 +1,22 @@
/*jshint browser:true, devel:true*/
if (!('sankore' in window)) {
window.sankore = {
preferences: {
state: ''
},
setPreference: function (name, value) {
console.log('Preference "' + name + '" set to : ' + value);
this.preferences[name] = value;
},
preference: function (name) {
console.log('Accessing "' + name + '"');
return this.preferences[name] || '';
},
locale: function () {
return window.navigator.language;
}
};
}

@ -0,0 +1,35 @@
/*global klass:true, Sankore:true*/
(function() {
"use strict";
klass.define('Sankore', 'Button', klass.extend({
constructor: function (text, command, useLimit, editable) {
this.text = text;
this.command = command;
this.useLimit = typeof useLimit === 'undefined' ? -1 : useLimit;
this.editable = typeof editable === 'undefined' ? true : editable;
},
isEditable: function () {
return this.editable;
},
isUsable: function () {
return this.useLimit === -1;
},
isDisabled: function () {
return this.useLimit === 0;
},
clone: function () {
return Sankore.Button.create(
this.text,
this.command,
this.useLimit,
this.editable
);
}
}));
})();

@ -0,0 +1,528 @@
/*jshint devel:true, browser:true*/
/*globals klass:true, Sankore:true, _:true */
(function () {
"use strict";
klass.define('Sankore', 'Calculator', klass.extend({
constructor: function (id, options) {
Sankore.Util.i18n.load(options.locale || 'en');
// state attributes
this.currentLayout = null;
this.lastError = null; // last raised error
this.memory = null; // internal memory
this.op = null; // op memory
this.output = null; // last evaluated resut
this.history = []; // operation history
this.buttonUseCount = {}; // button use count
// local attributes
this.expressionString = ''; // current expressionString
this.unpredictable = options.unpredictableMode || false;
// components
this.eventDispatcher = Sankore.Util.EventDispatcher.create();
this.commands = Sankore.Util.Hash.create(); // command hash
this.texts = Sankore.Util.Hash.create(); // text hash
this.calculusEngine = Sankore.Calculus.Engine.create();
this.keystrokeLine = Sankore.KeystrokeLine.create(this.eventDispatcher);
this.ui = Sankore.UI.MainInterface.create(id, this.eventDispatcher, this.texts, this.unpredictable);
this.editor = Sankore.Editor.Editor.create(this);
if (typeof options.ready !== 'undefined') {
this.eventDispatcher.addEventListener('calculator.create', options.ready.bind(this));
}
var c = Sankore.Text.create.bind(Sankore.Text);
this.texts.add('id', [
c('0', '0', '0'),
c('1', '1', '1'),
c('2', '2', '2'),
c('3', '3', '3'),
c('4', '4', '4'),
c('5', '5', '5'),
c('6', '6', '6'),
c('7', '7', '7'),
c('8', '8', '8'),
c('9', '9', '9'),
c('+', '+', '+', 'alt'),
c('-', '-', '-', 'alt'),
c('*', '\u00D7', '\u00D7', 'alt'),
c('/', '\u00F7', '\u00F7', 'alt'),
c(':', '\u22A2', '\u22A2', 'alt'),
c('=', '=', '=', 'alt'),
c('.', _('text.comma'), _('text.comma')),
c('(', '(', '('),
c(')', ')', ')'),
c('op', 'OP', 'OP'),
c('mr', 'MR', 'MR'),
c('mc', 'MC', 'MC'),
c('m+', 'M+', 'M+'),
c('m-', 'M-', 'M-'),
c('s', '', _('text.del'), 'alt', false),
c('l', '', '\u2190', 'alt', false),
c('r', '', '\u2192', 'alt', false),
c('c', '', 'C', 'danger', false)
]);
c = Sankore.Command.create.bind(Sankore.Command);
this.commands.add('id', [
// add 0 to the expression string
c('0', _('command.zero'), function (args) {
this.expressionString += '0';
}),
// add 1 to the expression string
c('1', _('command.one'), function (args) {
this.expressionString += '1';
}),
// add 2 to the expression string
c('2', _('command.two'), function (args) {
this.expressionString += '2';
}),
// add 3 to the expression string
c('3', _('command.three'), function (args) {
this.expressionString += '3';
}),
// add 4 to the expression string
c('4', _('command.four'), function (args) {
this.expressionString += '4';
}),
// add 5 to the expression string
c('5', _('command.five'), function (args) {
this.expressionString += '5';
}),
// add 6 to the expression string
c('6', _('command.six'), function (args) {
this.expressionString += '6';
}),
// add 7 to the expression string
c('7', _('command.seven'), function (args) {
this.expressionString += '7';
}),
// add 8 to the expression string
c('8', _('command.eight'), function (args) {
this.expressionString += '8';
}),
// add 9 to the expression string
c('9', _('command.nine'), function (args) {
this.expressionString += '9';
}),
// add + to the expression string
c('+', _('command.plus'), function (args) {
this.expressionString += '+';
}),
// add - to the expression string
c('-', _('command.minus'), function (args) {
this.expressionString += '-';
}),
// add * to the expression string
c('*', _('command.times'), function (args) {
this.expressionString += '*';
}),
// add / to the expression string
c('/', _('command.divide'), function (args) {
this.expressionString += '/';
}),
// add : to the expression string
c(':', _('command.euclidean_divide'), function (args) {
this.expressionString += ':';
}),
// evaluate the current expression
Sankore.InterruptingCommand.create('=', _('command.equal'), function (args) {
this.evaluateStack();
}),
// add . to the expression string
c('.', _('command.comma'), function (args) {
this.expressionString += '.';
}),
// add ( to the expression string
c('(', _('command.open_parenthesis'), function (args) {
this.expressionString += '(';
}),
// add ) to the expression string
c(')', _('command.close_parenthesis'), function (args) {
this.expressionString += ')';
}),
// store the current (incomplete) expression if not already, else
// call the incomplete expression, append it to the expression string then evaluate
Sankore.InterruptingCommand.create('op', _('command.op'), function (args) {
if (null === this.op) {
if (this.expressionString.length > 1 &&
-1 !== '+-*/:'.indexOf(this.expressionString[0])
) {
try {
// we parse the OP expression with a fake left operand (1)
// if it raised exceptions during parsing or evaluating,
// the expression is not good for an OP operation
this.calculusEngine.evaluate('(1)' + this.expressionString).getValue();
this.op = this.expressionString;
this.eventDispatcher.notify('calculator.op_changed', this.op);
} catch (e) {
}
}
this.expressionString = '';
this.output = null;
} else {
// case : the OP command is called immediately. The previous results is used
// as the left operand
if (0 === this.expressionString.length && null !== this.output) {
this.expressionString = '(' + this.output.toString() + ')';
}
this.expressionString += this.op;
this.execCommand('=');
}
}),
// evaluate the expression string and add it to the memory
Sankore.InterruptingCommand.create('memoryAdd', _('command.memory_add'), function (args) {
this.execCommand('=');
try {
if (null === this.memory) {
this.memory = 0;
}
this.memory += this.output.getValue();
} catch (e) {
this.memory = null;
}
this.eventDispatcher.notify('calculator.memory_changed', this.memory);
}),
// evaluate the expression string and substract it from the memory
Sankore.InterruptingCommand.create('memorySub', _('command.memory_sub'), function (args) {
this.execCommand('=');
try {
if (null === this.memory) {
this.memory = 0;
}
this.memory -= this.output.getValue();
} catch (e) {
this.memory = null;
}
this.eventDispatcher.notify('calculator.memory_changed', this.memory);
}),
// add the current memory value to the expression string
c('memoryRecall', _('command.memory_recall'), function (args) {
if (null !== this.memory) {
this.expressionString += '(' + this.memory.toString() + ')';
}
}),
// clear the memory
c('memoryClear', _('command.memory_clear'), function (args) {
this.memory = null;
this.eventDispatcher.notify('calculator.memory_changed', this.memory);
}),
// clear the output, memory, op and expression string
Sankore.InternalCommand.create('clear', _('command.clear'), function (args) {
this.reset();
this.eventDispatcher.notify('calculator.memory_changed', this.memory);
this.eventDispatcher.notify('calculator.op_changed', this.op);
this.eventDispatcher.notify('calculator.output_changed', {
output: this.output,
error: this.lastError
});
}),
// move the caret left
Sankore.InternalCommand.create('left', _('command.left'), function (args) {
this.keystrokeLine.moveCaretLeft();
}),
// move the caret right
Sankore.InternalCommand.create('right', _('command.right'), function (args) {
this.keystrokeLine.moveCaretRight();
}),
// delete the keystroke before the caret
Sankore.InternalCommand.create('del', _('command.del'), function (args) {
var keystroke = this.keystrokeLine.del();
if (keystroke && this.getButtonUseCount(keystroke.slot)) {
this.buttonUseCount[keystroke.slot]--;
if (this.getButtonUseCount(keystroke.slot) < this.currentLayout.getButton(keystroke.slot).useLimit) {
this.eventDispatcher.notify('calculator.button_enabled', keystroke.slot);
}
}
})
]);
this.attachEventHandlers();
this.eventDispatcher.notify('calculator.create');
},
attachEventHandlers: function () {
var self = this, ed = this.eventDispatcher;
if (this.unpredictable) {
ed.addEventListener('editor.show', this.reload.bind(this));
ed.addEventListener('editor.hide', this.reload.bind(this));
}
ed.addEventListener('editor.layout_selected', function (layout) {
self.loadLayout(layout.id);
});
ed.addEventListener('main_interface.button_created', function (event) {
if (!self.editor.enabled && event.button.isDisabled()) {
ed.notify('calculator.button_disabled', event);
}
});
ed.addEventListener('main_interface.button_click', function (event) {
if (!self.editor.enabled) {
self.useButton(event.slot);
self.keystroke(event.slot, event.button);
if (!event.button.isUsable() && self.getButtonUseCount(event.slot) >= event.button.useLimit) {
ed.notify('calculator.button_disabled', event);
}
}
});
ed.addEventListener('main_interface.reset_click', function () {
self.reload();
});
},
reset: function () {
this.memory = null;
this.op = null;
this.output = null;
this.lastError = null;
this.expressionString = [];
this.keystrokeLine.reset();
},
init: function (state) {
var self = this;
this.ui.render();
this.editor.init(state.editor ||  {});
if (null !== this.layout) {
this.loadLayout(state.layout || 'default');
}
if ('buttonUseCount' in state) {
this.buttonUseCount = state.buttonUseCount;
}
if ('keystrokes' in state) {
this.keystrokeLine.loadState(state.keystrokes || {});
}
if ('output' in state && state.output) {
this.expressionString = state.output || '';
this.evaluateStack();
}
if ('error' in state && state.error) {
this.lastError = Sankore.Util.Error.create(state.error.name, state.error.message);
this.eventDispatcher.notify('calculator.output_changed', {
output: this.output,
error: this.lastError
});
}
if ('memory' in state) {
this.memory = state.memory;
this.eventDispatcher.notify('calculator.memory_changed', this.memory);
}
if ('op' in state) {
this.op = state.op;
this.eventDispatcher.notify('calculator.op_changed', this.op);
}
if ('history' in state && state.history.length > 0) {
this.history = state.history.map(function (log) {
return {
expression: log.expression,
output: self.calculusEngine.evaluate(log.output)
};
});
this.eventDispatcher.notify('calculator.history_changed', this.history);
}
this.eventDispatcher.notify('calculator.init');
},
getState: function () {
return {
layout: this.currentLayout ? this.currentLayout.id : null,
error: this.lastError,
memory: this.memory,
op: this.op,
output: this.output ? this.output.toString() : null,
buttonUseCount: this.buttonUseCount,
editor: this.editor.getState(),
keystrokes: this.keystrokeLine.getState(),
history: this.history.map(function (log) {
return {
expression: log.expression,
output: log.output.toString()
};
})
};
},
loadLayout: function (layoutId) {
this.currentLayout = this.editor.layouts.get(layoutId);
this.execCommand('clear');
this.buttonUseCount = {};
this.history = [];
this.eventDispatcher.notify('calculator.layout_loaded', this.currentLayout);
},
reload: function () {
this.loadLayout(this.currentLayout.id);
},
execCommand: function (name, args) {
var command = this.commands.get(name);
command.exec(this, args);
this.eventDispatcher.notify('calculator.command_executed', {
command: command,
args: args
});
},
evaluateStack: function () {
try {
if (this.expressionString.length > 0) {
this.output = this.calculusEngine.evaluate(this.expressionString);
} else {
this.output = null;
}
this.lastError = null;
} catch (e) {
this.lastError = e;
this.output = null;
}
this.expressionString = '';
this.eventDispatcher.notify('calculator.output_changed', {
output: this.output,
error: this.lastError
});
},
keystroke: function (slot, button) {
var command = this.commands.get(button.command),
text = this.texts.get(button.text);
if (!command.isInternal()) {
if (command.isInterrupting()) {
this.keystrokeLine.moveCaretToEnd();
}
this.keystrokeLine.hit({
slot: slot,
text: text.screen,
command: button.command
});
} else {
this.execCommand(command.id);
}
if (command.isInterrupting()) {
this.execute();
}
},
execute: function () {
var length = this.keystrokeLine.count(),
i;
for (i = 0; i < length; i++) {
this.execCommand(this.keystrokeLine.at(i).command);
}
this.eventDispatcher.notify('calculator.executed');
if (!this.lastError && this.output) {
try {
this.backup(
this.keystrokeLine.getAsText().join(''),
this.output
);
} catch (e) {}
}
this.keystrokeLine.reset();
},
useButton: function (slot) {
if (typeof this.buttonUseCount[slot] === 'undefined') {
this.buttonUseCount[slot] = 0;
}
this.buttonUseCount[slot]++;
},
getButtonUseCount: function (slot) {
return this.buttonUseCount[slot] || 0;
},
backup: function (expression, output) {
try {
// precompute output value for raising potentials exceptions, ugly hack though
output.getValue();
this.history.push({
expression: expression,
output: output
});
this.eventDispatcher.notify('calculator.history_changed', this.history);
} catch (e) {}
}
}));
})();

@ -0,0 +1,155 @@
/*jshint plusplus: true*/
/*global klass: true, Sankore: true*/
(function () {
"use strict";
klass.define('Sankore.Calculus', 'Engine', klass.extend({
constructor: function () {
this.expressions = [];
this.operators = [];
},
init: function () {
this.expressions = [];
this.operators = [];
},
evaluate: function (expressionString) {
var tokens = [],
lastToken,
penultimateToken,
item;
expressionString = expressionString.replace(/\s+/g, '');
for (var i in expressionString) {
item = expressionString[i];
if (tokens.length > 0) {
lastToken = tokens[tokens.length - 1];
} else {
lastToken = undefined;
}
if (tokens.length > 1) {
penultimateToken = tokens[tokens.length - 2];
} else {
penultimateToken = undefined;
}
if ('0123456789.'.indexOf(item) !== -1) {
if (
!isNaN(Number(lastToken)) ||
lastToken === '.' ||
lastToken === '-.' ||
(
lastToken === '-' &&
(
penultimateToken === '(' ||
penultimateToken === undefined
)
)
) {
tokens[tokens.length - 1] += item;
} else {
tokens.push(item);
}
} else {
tokens.push(item);
}
}
for (var j in tokens) {
if (tokens[j].length > 1 && tokens[j].charAt(tokens[j].length - 1) === '.') {
throw Sankore.Util.Error.create('InvalidExpression', 'Trailing comma');
}
}
return this.computeExpression(tokens);
},
computeExpression: function (tokens) {
var operatorCheck = function (stack, token) {
var prec = Sankore.Calculus.BinaryOperation.getOperatorPrecedence,
top;
while (true) {
top = stack.operators[stack.operators.length - 1];
if (stack.operators.length === 0 || top === '(' || prec(token) > prec(top)) {
return stack.operators.push(token);
}
stack.reduce();
}
};
this.init();
for (var i in tokens) {
switch (tokens[i]) {
case '(':
this.operators.push(tokens[i]);
break;
case ')':
if (this.operators.length === 0) {
throw Sankore.Util.Error.create('InvalidExpression', 'Trailing closing brackets');
}
while (this.operators.length !== 0 && this.operators[this.operators.length - 1] !== '(') {
this.reduce();
}
if (this.operators[this.operators.length - 1] === '(') {
this.operators.pop(); // get rid of the extra paren '('
}
break;
case '+':
case '-':
case '*':
case '/':
case ':':
operatorCheck(this, tokens[i]);
break;
default:
this.expressions.push(Sankore.Calculus.Operand.create(tokens[i]));
}
}
while (this.operators.length !== 0) {
this.reduce();
}
// if there's not one and only one expression in the stack, the expression is invalid
if (this.expressions.length !== 1) {
throw Sankore.Util.Error.create('InvalidExpression', '"' + tokens.join(' ') + '" is not a valid expression');
}
return this.expressions.pop();
},
reduce: function () {
var right = this.expressions.pop(),
left = this.expressions.pop(),
operator = this.operators.pop(),
operation;
if (typeof operator === 'undefined' || typeof left === 'undefined' || typeof right === 'undefined') {
throw Sankore.Util.Error.create('InvalidExpression', 'Invalid expression');
}
if (operator === ':') {
operation = Sankore.Calculus.EuclideanDivisionOperation.create(left, right);
} else {
operation = Sankore.Calculus.BinaryOperation.create(left, operator, right);
}
this.expressions.push(operation);
}
}));
})();

@ -0,0 +1,217 @@
/*global klass:true, Sankore:true */
(function () {
"use strict";
/**
* Base class for expression
*/
klass.define('Sankore.Calculus', 'Expression', klass.extend({
constructor: function () {
},
getValue: function () {
return null;
},
isInteger: function () {
try {
var value = this.getValue();
return value === Math.floor(value);
} catch (e) {
return 0;
}
},
toString: function () {
return '';
},
isCompound: function () {
return false;
}
}));
/**
* Calculus operand
*/
klass.define('Sankore.Calculus', 'Operand', Sankore.Calculus.Expression.extend({
constructor: function (value) {
this.value = Number(value);
if (isNaN(this.value)) {
throw Sankore.Util.Error.create('InvalidNumber', '"' + String(value) + '" is a not a number');
}
},
getValue: function () {
return this.value;
},
toString: function () {
return String(this.value);
}
}));
/**
* Unary operator (+, -)
*/
klass.define('Sankore.Calculus', 'Operation', Sankore.Calculus.Expression.extend({
constructor: function (operator, right) {
this.operator = operator;
if (!Sankore.Calculus.Expression.isPrototypeOf(right)) {
right = Sankore.Calculus.Operand.create(right);
}
this.right = right;
},
getPrecedence: function () {
return Sankore.Calculus.Operation.getOperatorPrecedence(this.operator);
},
isLeftAssociative: function () {
return false;
},
isCompound: function () {
return true;
},
getValue: function () {
var value = Number(this.right.getValue());
if (this.operator === '-') {
value *= -1;
}
return value;
},
toString: function () {
var string = this.right.toString();
if (this.operator !== '-') {
return string;
}
if (this.right.isCompound()) {
string = '(' + string + ')';
}
return '-' + string;
}
}));
Sankore.Calculus.Operation.getOperatorPrecedence = function (operator) {
switch (operator) {
case '+':
case '-':
return 1;
case '*':
case '/':
case ':':
return 2;
}
};
/**
* Binary operator (+, -, *, /)
*/
klass.define('Sankore.Calculus', 'BinaryOperation', Sankore.Calculus.Operation.extend({
constructor: function (left, operator, right) {
Sankore.Calculus.Operation.constructor.call(this, operator, right);
if (!Sankore.Calculus.Expression.isPrototypeOf(left)) {
left = Sankore.Calculus.Operand.create(left);
}
this.left = left;
},
isLeftAssociative: function () {
return true;
},
getValue: function () {
var leftValue = this.left.getValue(),
rightValue = this.right.getValue();
switch (this.operator) {
case '+':
return leftValue + rightValue;
case '-':
return leftValue - rightValue;
case '*':
return leftValue * rightValue;
case '/':
if (0 === rightValue) {
throw Sankore.Util.Error.create('ZeroDivision', 'Division by zero');
}
return leftValue / rightValue;
default:
throw Sankore.Util.Error.create('InvalidOperator', 'This is not a valid operator : ' + this.operator);
}
},
toString: function () {
if (this.isInteger()) {
return String(this.getValue());
}
var leftString = this.left.toString(),
rightString = this.right.toString(),
string = '';
if (Sankore.Calculus.Operation.isPrototypeOf(this.left)) {
if (this.left.getPrecedence() < this.getPrecedence()) {
leftString = '(' + leftString + ')';
}
}
if (Sankore.Calculus.Operation.isPrototypeOf(this.right)) {
if (this.right.getPrecedence() < this.getPrecedence()) {
rightString = '(' + rightString + ')';
}
}
return leftString + String(this.operator) + rightString;
}
}));
/**
* Euclidean division operator
*/
klass.define('Sankore.Calculus', 'EuclideanDivisionOperation', Sankore.Calculus.BinaryOperation.extend({
constructor: function (left, right) {
Sankore.Calculus.BinaryOperation.constructor.call(this, left, ':', right);
},
getValue: function () {
var rightValue = this.right.getValue();
if (0 === rightValue) {
throw Sankore.Util.Error.create('ZeroDivision', 'Division by zero');
}
return Math.floor(this.left.getValue() / rightValue);
},
getRemainder: function () {
var rightValue = this.right.getValue();
if (0 === rightValue) {
throw Sankore.Util.Error.create('ZeroDivision', 'Division by zero');
}
return this.left.getValue() % rightValue;
}
}));
})();

@ -0,0 +1,52 @@
/*global klass:true, Sankore:true*/
(function () {
"use strict";
klass.define('Sankore', 'Command', klass.extend({
constructor: function (id, name, closure) {
this.id = id;
this.name = name;
this.closure = closure;
},
getId: function () {
return this.id;
},
getName: function () {
return this.name;
},
exec: function (scope, args) {
this.closure.call(scope, args);
},
isInterrupting: function () {
return false;
},
isInternal: function () {
return false;
}
}));
klass.define('Sankore', 'InterruptingCommand', Sankore.Command.extend({
constructor: function (id, name, closure) {
Sankore.Command.constructor.call(this, id, name, closure);
},
isInterrupting: function () {
return true;
}
}));
klass.define('Sankore', 'InternalCommand', Sankore.Command.extend({
constructor: function (id, name, closure) {
Sankore.Command.constructor.call(this, id, name, closure);
},
isInternal: function () {
return true;
}
}));
})();

@ -0,0 +1,341 @@
/*jshint browser:true, devel:true */
/*global klass:true, Sankore:true, _:true*/
(function () {
"use strict";
klass.define('Sankore.Editor', 'Editor', klass.extend({
constructor: function (calculator) {
// state attributes
this.current = null;
this.activeButton = null;
this.enabled = false;
this.layouts = Sankore.Util.Hash.create({
'default': Sankore.Editor.Layout.create({
id: 'default',
name: _('layout.classic_name'),
buttonMap: {
a1: Sankore.Button.create('mr', 'memoryRecall'),
b1: Sankore.Button.create('mc', 'memoryClear'),
c1: Sankore.Button.create('m+', 'memoryAdd'),
d1: Sankore.Button.create('m-', 'memorySub'),
a2: Sankore.Button.create('op', 'op'),
b2: Sankore.Button.create('(', '('),
c2: Sankore.Button.create(')', ')'),
d2: Sankore.Button.create(':', ':'),
a3: Sankore.Button.create('7', '7'),
b3: Sankore.Button.create('8', '8'),
c3: Sankore.Button.create('9', '9'),
d3: Sankore.Button.create('/', '/'),
a4: Sankore.Button.create('4', '4'),
b4: Sankore.Button.create('5', '5'),
c4: Sankore.Button.create('6', '6'),
d4: Sankore.Button.create('*', '*'),
a5: Sankore.Button.create('1', '1'),
b5: Sankore.Button.create('2', '2'),
c5: Sankore.Button.create('3', '3'),
d5: Sankore.Button.create('-', '-'),
a6: Sankore.Button.create('0', '0'),
b6: Sankore.Button.create('.', '.'),
c6: Sankore.Button.create('=', '='),
d6: Sankore.Button.create('+', '+')
}
})
});
// components
this.calculator = calculator;
this.ui = Sankore.UI.EditorInterface.create(this, this.calculator.eventDispatcher);
this.attachEventHandlers();
},
attachEventHandlers: function () {
var self = this, ed = this.calculator.eventDispatcher;
// click on Add button
ed.addEventListener('editor_interface.add_click', function () {
var clone = self.createLayout();
self.setCurrentLayout(clone.id);
});
// click on Remove button
ed.addEventListener('editor_interface.remove_click', function () {
self.removeLayout(self.current);
self.setCurrentLayout('default');
});
// click on Run button
ed.addEventListener('editor_interface.run_click', this.runCurrentLayout.bind(this));
// load the new selected layout
ed.addEventListener('editor_interface.layout_select', function (layoutId) {
self.setCurrentLayout(layoutId);
});
// the layout name has changed
ed.addEventListener('editor_interface.layout_name_change', function (name) {
if (self.getCurrentLayout().name !== name && name.trim().length > 0) {
self.getCurrentLayout().name = name;
ed.notify('editor.layout_changed');
}
});
// the layout description has changed
ed.addEventListener('editor_interface.layout_description_change', function (description) {
if (self.getCurrentLayout().description !== description) {
self.getCurrentLayout().description = description;
ed.notify('editor.layout_changed');
}
});
// the command of a button has changed
ed.addEventListener('editor_interface.button_command_change', function (command) {
if (self.activeButton) {
self.getCurrentLayout().getButton(self.activeButton).command = command;
ed.notify('editor.layout_changed');
}
});
// the text of a button has changed
ed.addEventListener('editor_interface.button_text_change', function (text) {
if (self.activeButton) {
var button = self.getCurrentLayout().getButton(self.activeButton);
button.text = text;
ed.notify('editor.button_renamed', {
slot: self.activeButton,
button: button
});
ed.notify('editor.layout_changed');
}
});
// the use limit of a button has changed
ed.addEventListener('editor_interface.button_uselimit_change', function (limit) {
if (self.activeButton) {
if (!isNaN(Number(limit))) {
self.getCurrentLayout().getButton(self.activeButton).useLimit = limit.length === 0 ? -1 : Number(limit);
ed.notify('editor.layout_changed');
}
}
});
// a button is clicked
ed.addEventListener('main_interface.button_click', function (event) {
if (self.enabled) {
self.setActiveButton(event.slot);
}
});
// the editor button is click
ed.addEventListener('main_interface.editor_click', function () {
if (self.enabled) {
self.runCurrentLayout();
} else {
self.enable();
}
});
// the editor button is click
ed.addEventListener('main_interface.reset_click', function () {
if (self.enabled) {
self.resetActiveButton();
}
});
},
init: function (state) {
var self = this;
if ('layouts' in state) {
this.loadLayouts(state.layouts);
}
if ('enabled' in state) {
this.enabled = state.enabled;
}
this.ui.render(this.calculator.ui);
this.setCurrentLayout(('current' in state && state.current) ? state.current : 'default');
if (this.enabled) {
this.enable();
}
if ('activeButton' in state && this.enabled) {
this.setActiveButton(state.activeButton);
}
},
getState: function () {
return {
current: this.current,
activeButton: this.activeButton,
enabled: this.enabled,
layouts: this.layouts.map(function (id, layout) {
if (layout.isEditable()) {
return layout;
}
})
};
},
loadLayouts: function (layouts) {
var buttonMap = {},
layout;
for (var i in layouts) {
for (var slot in layouts[i].buttonMap) {
if (layouts[i].buttonMap.hasOwnProperty(slot)) {
buttonMap[slot] = Sankore.Button.create(
layouts[i].buttonMap[slot].text,
layouts[i].buttonMap[slot].command,
layouts[i].buttonMap[slot].useLimit
);
}
}
layout = Sankore.Editor.Layout.create({
id: layouts[i].id,
name: layouts[i].name,
description: layouts[i].description,
buttonMap: buttonMap
});
layout.setEditable(true);
this.layouts.add('id', [layout]);
}
},
getCurrentLayout: function () {
if (null === this.current) {
return null;
}
return this.layouts.get(this.current);
},
setCurrentLayout: function (id) {
this.current = id;
this.calculator.eventDispatcher.notify('editor.layout_selected', this.getCurrentLayout());
this.resetActiveButton();
},
createLayout: function () {
var clone = this.layouts.get('default').clone();
clone.id = this.generateId();
clone.name = _('layout.new_name');
clone.setEditable(true);
this.layouts.set(clone.id, clone);
this.calculator.eventDispatcher.notify('editor.layout_created');
return clone;
},
/**
* don't try to understand the purpose of this method, it just generates an unique string based upon the current widget url and current time
*/
generateId: function () {
var values = '',
date = new Date(),
id = 0,
i;
for (i = 0; i < document.URL.length; i++) {
values += String(document.URL.charCodeAt(i) * (date.getMilliseconds() + date.getSeconds() + date.getMinutes()));
}
values = values.match(/.{1,10}/g);
for (i in values) {
id += Number(values[i]);
}
return id.toString(36);
},
removeLayout: function (id) {
if (confirm(_('editor.remove_alert'))) {
this.layouts.remove(id);
this.calculator.eventDispatcher.notify('editor.layout_removed');
}
},
setActiveButton: function (slot) {
if (slot && this.getCurrentLayout().isEditable()) {
var button = this.getCurrentLayout().getButton(slot);
if (button.isEditable()) {
this.calculator.eventDispatcher.notify('editor.button_selected', {
slot: slot,
button: button,
previousSlot: this.activeButton
});
this.activeButton = slot;
}
} else {
this.resetActiveButton();
}
},
resetActiveButton: function () {
this.calculator.eventDispatcher.notify('editor.button_selected', {
slot: null,
button: null,
previousSlot: this.activeButton
});
this.activeButton = null;
},
enable: function () {
this.enabled = true;
this.setActiveButton(null);
this.calculator.eventDispatcher.notify('editor.show');
},
disable: function () {
this.enabled = false;
this.setActiveButton(null);
this.calculator.eventDispatcher.notify('editor.hide');
},
getAssignableCommands: function () {
return this.calculator.commands.map(function (k, v) {
if (!v.isInternal()) {
return v;
}
});
},
getAssignableTexts: function () {
return this.calculator.texts.map(function (k, v) {
if (v.isEditable()) {
return v;
}
});
},
runCurrentLayout: function () {
this.disable();
}
}));
})();

@ -0,0 +1,44 @@
/*global klass:true, Sankore:true*/
(function () {
"use strict";
klass.define('Sankore.Editor', 'Layout', klass.extend({
constructor: function (data) {
this.id = data.id || null;
this.name = data.name || null;
this.description = data.description || null;
this.editable = false;
this.buttonMap = data.buttonMap || {};
},
setEditable: function (editable) {
this.editable = !! editable;
},
isEditable: function () {
return this.editable;
},
getButton: function (slot) {
return this.buttonMap[slot] || null;
},
clone: function () {
var clonedMap = {};
for (var index in this.buttonMap) {
if (this.buttonMap.hasOwnProperty(index)) {
clonedMap[index] = this.buttonMap[index].clone();
}
}
return Sankore.Editor.Layout.create({
id: this.id,
name: this.name,
description: this.description,
editable: this.editable,
buttonMap: clonedMap
});
}
}));
})();

@ -0,0 +1,109 @@
/*global klass:true, Sankore:true*/
(function () {
"use strict";
klass.define('Sankore', 'KeystrokeLine', klass.extend({
constructor: function (dispatcher) {
this.dispatcher = dispatcher;
this.keystrokes = [];
this.caret = 0;
},
notify: function () {
this.dispatcher.notify('keystroke_line.changed', this);
},
hit: function (keystroke) {
this.keystrokes.splice(this.caret, 0, keystroke);
this.caret++;
this.notify();
},
del: function () {
if (this.caret > 0) {
var deleted = this.keystrokes.splice(this.caret - 1, 1)[0];
this.caret--;
this.notify();
return deleted;
}
},
moveCaretLeft: function () {
if (this.caret > 0) {
this.caret--;
this.notify();
}
},
moveCaretRight: function () {
if (this.caret < this.keystrokes.length) {
this.caret++;
this.notify();
}
},
moveCaretToEnd: function () {
this.caret = this.keystrokes.length;
this.notify();
},
reset: function () {
this.caret = 0;
this.keystrokes = [];
this.notify();
},
count: function () {
return this.keystrokes.length;
},
at: function (index) {
if (typeof this.keystrokes[index] !== 'undefined') {
return this.keystrokes[index];
}
throw Sankore.Util.Error.create('OutOfRangeError', 'No keystroke at index ' + index);
},
getAsText: function () {
return [
this.getTextAtRange(0, this.caret),
this.getTextAtRange(this.caret, this.keystrokes.length)
];
},
getTextAtRange: function (from, to) {
var i, output = '';
if (from < 0) {
throw Sankore.Util.Error.create('OutOfRangeError', 'Cannot get keystroke before index 0');
}
if (from > this.keystrokes.length) {
throw Sankore.Util.Error.create('OutOfRangeError', 'Cannot get keystroke after index ' + this.keystrokes.length);
}
for (i = from; i < to; i++) {
output += this.at(i).text;
}
return output;
},
getState: function () {
return {
keystrokes: this.keystrokes,
caret: this.caret
};
},
loadState: function (state) {
this.keystrokes = state.keystrokes || {};
this.caret = state.caret || 0;
this.notify();
}
}));
})();

@ -0,0 +1,23 @@
/*globals klass:true, Sankore:true*/
(function() {
"use strict";
klass.define('Sankore', 'Text', klass.extend({
constructor: function (id, screen, button, type, editable) {
this.id = id;
this.screen = screen;
this.button = button;
this.type = typeof type !== 'undefined' ? type : 'normal';
this.editable = typeof editable !== 'undefined' ? !!editable : true;
},
isEditable: function () {
return this.editable;
},
setEditable: function (editable) {
this.editable = !!editable;
}
}));
})();

@ -0,0 +1,323 @@
/*jshint browser:true*/
/*global klass:true, Sankore: true, _:true*/
(function () {
"use strict";
klass.define('Sankore.UI', 'EditorInterface', klass.extend({
constructor: function (editor, ed) {
this.editor = editor;
this.dispatcher = ed;
this.hidden = true;
this.editArea = null;
this.layoutSelect = null;
this.layoutNameInput = null;
this.layoutDescriptionInput = null;
this.assignationDiv = null;
this.runButton = null;
this.addButton = null;
this.removeButton = null;
this.attachEventListeners();
this.rendered = false;
},
attachEventListeners: function () {
var self = this;
this.dispatcher.addEventListener('editor.show', this.show.bind(this));
this.dispatcher.addEventListener('editor.hide', this.hide.bind(this));
this.dispatcher.addEventSubscriber({
events: ['editor.layout_changed', 'editor.layout_created', 'editor.layout_removed'],
listener: this.updateLayoutSelectElement.bind(this)
});
this.dispatcher.addEventListener('editor.layout_selected', function (layout) {
self.loadLayout(layout);
self.selectLayout(layout.id);
});
this.dispatcher.addEventListener('editor.button_selected', function (e) {
var context = e.button;
if (null === context && self.editor.getCurrentLayout()) {
context = self.editor.getCurrentLayout().isEditable();
}
self.renderAssignation(context);
});
},
_clearElement: function (element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
},
_map: function (iterable, callback) {
var mapped = [],
idx;
for (idx in iterable) {
if (iterable.hasOwnProperty(idx)) {
mapped.push(callback.call(iterable, iterable[idx], idx));
}
}
return mapped;
},
render: function (mainInterface) {
var layoutNameLabel,
layoutDescriptionLabel,
assignationDiv,
assignationText,
self = this;
this.editArea = document.createElement('div');
this.editArea.classList.add('edit-area');
this.layoutSelect = this.createLayoutSelectElement();
this.editArea.appendChild(this.layoutSelect);
this.addButton = document.createElement('button');
this.addButton.className = 'small';
this.addButton.setAttribute('type', 'button');
this.addButton.addEventListener('click', function (e) {
self.dispatcher.notify('editor_interface.add_click');
});
this.removeButton = this.addButton.cloneNode();
this.removeButton.classList.add('remove');
this.removeButton.addEventListener('click', function (e) {
self.dispatcher.notify('editor_interface.remove_click');
});
this.addButton.classList.add('add');
this.editArea.appendChild(this.addButton);
this.editArea.appendChild(this.removeButton);
this.editArea.appendChild(document.createElement('hr'));
layoutNameLabel = document.createElement('label');
layoutNameLabel.appendChild(document.createTextNode(_('editor.layout_name.label')));
this.editArea.appendChild(layoutNameLabel);
this.layoutNameInput = document.createElement('input');
this.layoutNameInput.setAttribute('name', 'layout_name');
this.layoutNameInput.setAttribute('type', 'text');
this.layoutNameInput.setAttribute('maxlength', 32);
this.layoutNameInput.addEventListener('keyup', function (e) {
self.dispatcher.notify('editor_interface.layout_name_change', this.value);
});
this.layoutNameInput.addEventListener('change', function (e) {
if (this.value.trim().length === 0) {
this.value = self.editor.getCurrentLayout().name;
self.dispatcher.notify('editor_interface.layout_name_change', this.value);
}
});
this.editArea.appendChild(this.layoutNameInput);
layoutDescriptionLabel = document.createElement('label');
layoutDescriptionLabel.appendChild(document.createTextNode(_('editor.layout_description.label')));
this.editArea.appendChild(layoutDescriptionLabel);
this.layoutDescriptionInput = document.createElement('textarea');
this.layoutDescriptionInput.setAttribute('name', 'layout_description');
this.layoutDescriptionInput.setAttribute('maxlength', 140);
this.layoutDescriptionInput.addEventListener('keyup', function(e) {
self.dispatcher.notify('editor_interface.layout_description_change', this.value);
});
this.editArea.appendChild(this.layoutDescriptionInput);
this.assignationDiv = document.createElement('div');
this.assignationDiv.classList.add('assignation');
this.editArea.appendChild(this.assignationDiv);
this.runButton = document.createElement('button');
this.runButton.classList.add('run');
this.runButton.setAttribute('type', 'button');
this.runButton.appendChild(document.createTextNode(_('editor.run_button')));
this.runButton.addEventListener('click', function (e) {
self.dispatcher.notify('editor_interface.run_click');
});
this.editArea.appendChild(this.runButton);
mainInterface.rearScreen.parentElement.appendChild(this.editArea);
this.rendered = true;
this.hide();
},
createSelectElement: function (data, name, className, selectedValue) {
var select = document.createElement('select'),
option;
select.setAttribute('name', name);
if (className) {
select.className = className;
}
for (var i in data) {
option = document.createElement('option');
option.setAttribute('value', data[i].value);
if (typeof selectedValue !== 'undefined' && selectedValue === data[i].value) {
option.selected = true;
}
option.appendChild(document.createTextNode(data[i].text));
select.appendChild(option);
}
return select;
},
createLayoutSelectElement: function () {
var select = this.createSelectElement(
this.editor.layouts.map(function (k, layout) {
return {
text: layout.name,
value: layout.id
};
}),
'layouts',
'layout-select',
this.editor.current
),
self = this;
select.addEventListener('change', function (e) {
self.dispatcher.notify('editor_interface.layout_select', this.value);
});
return select;
},
updateLayoutSelectElement: function () {
var select = this.createLayoutSelectElement();
this.editArea.replaceChild(select, this.layoutSelect);
this.layoutSelect = select;
},
selectLayout: function (selected) {
this.layoutSelect.value = selected;
},
show: function () {
if (this.rendered) {
this.editArea.style.display = 'block';
this.hidden = false;
}
},
hide: function () {
if (this.rendered) {
this.editArea.style.display = 'none';
this.hidden = true;
}
},
loadLayout: function (layout) {
this.layoutNameInput.value = layout.name;
this.layoutNameInput.disabled = !layout.isEditable();
this.layoutDescriptionInput.value = layout.description;
this.layoutDescriptionInput.disabled = !layout.isEditable();
this.removeButton.disabled = !layout.isEditable();
this.renderAssignation(layout.isEditable());
},
renderAssignation: function (context) {
var innerEl, textLabel, textSelect, commandLabel, commandSelect, useLabel, useInput, useHelp,
text,
self = this;
if (false === context) {
innerEl = document.createElement('em');
innerEl.appendChild(document.createTextNode(_('editor.assignation.disabled')));
} else if (Sankore.Button.isPrototypeOf(context)) {
innerEl = document.createDocumentFragment();
textLabel = document.createElement('label');
textLabel.appendChild(document.createTextNode(_('editor.assignation.text.label')));
innerEl.appendChild(textLabel);
text = this.editor.calculator.texts.get(context.text);
textSelect = this.createSelectElement(
this._map(this.editor.getAssignableTexts(), function (text) {
return {
text: text.button,
value: text.id
};
}),
'button_text',
'',
text.id
);
textSelect.addEventListener('change', function (e) {
self.dispatcher.notify('editor_interface.button_text_change', e.target.value);
});
innerEl.appendChild(textSelect);
commandLabel = document.createElement('label');
commandLabel.appendChild(document.createTextNode(_('editor.assignation.command.label')));
innerEl.appendChild(commandLabel);
commandSelect = this.createSelectElement(
this._map(this.editor.getAssignableCommands(), function (command) {
return {
text: command.name,
value: command.id
};
}),
'button_command',
'',
context.command
);
commandSelect.addEventListener('change', function (e) {
self.dispatcher.notify('editor_interface.button_command_change', e.target.value);
});
innerEl.appendChild(commandSelect);
useLabel = document.createElement('label');
useLabel.appendChild(document.createTextNode(_('editor.assignation.use_limit.label')));
innerEl.appendChild(useLabel);
useInput = document.createElement('input');
useInput.setAttribute('type', 'text');
useInput.setAttribute('name', 'button_count');
useInput.value = (context.useLimit === -1 ? '' : context.useLimit);
useInput.addEventListener('change', function (e) {
self.dispatcher.notify('editor_interface.button_uselimit_change', e.target.value);
});
innerEl.appendChild(useInput);
useHelp = document.createElement('span');
useHelp.className = 'help';
useHelp.appendChild(document.createTextNode(_('editor.assignation.use_limit.help')));
innerEl.appendChild(useHelp);
} else {
innerEl = document.createElement('em');
innerEl.appendChild(document.createTextNode(_('editor.assignation.enabled')));
}
this._clearElement(this.assignationDiv);
this.assignationDiv.appendChild(innerEl);
}
}));
})();

@ -0,0 +1,534 @@
/*jshint browser:true */
/*global klass:true, Sankore:true, _:true*/
(function () {
"use strict";
klass.define('Sankore.UI', 'MainInterface', klass.extend({
constructor: function (id, ed, texts, withEditor) {
this.id = id;
this.dispatcher = ed;
this.texts = texts;
this.withEditor = withEditor;
this.title = null;
this.caret = document.createElement('i');
this.caret.className = 'caret';
this.frontScreen = null;
this.expressionRow = null;
this.resultRow = null;
this.flagRow = null;
this.flags = [];
this.rearScreen = null;
this.buttons = Sankore.Util.Hash.create();
this.rendered = false;
this.attachEventListeners();
},
attachEventListeners: function () {
var self = this;
// the main screen text has changed
this.dispatcher.addEventListener('keystroke_line.changed', function (keystrokeLine) {
self.updateExpressionRow(keystrokeLine.getAsText());
});
// the internal memory has changed
this.dispatcher.addEventListener('calculator.memory_changed', function (memory) {
if (null !== memory) {
self.addFlag('M');
} else {
self.removeFlag('M');
}
self.updateFlagRow();
});
// the op memory has changed
this.dispatcher.addEventListener('calculator.op_changed', function (op) {
if (null !== op) {
self.addFlag('OP');
} else {
self.removeFlag('OP');
}
self.updateFlagRow();
});
// a new result has been computed
this.dispatcher.addEventListener('calculator.output_changed', function (event) {
if (null !== event.output) {
self.updateResultRow(event.output);
} else if (null !== event.error) {
self.updateResultRow(event.error);
} else {
self.updateResultRow('');
}
});
// the history has changed
this.dispatcher.addEventListener('calculator.history_changed', this.updateRearScreen.bind(this));
// a new layout is loaded
this.dispatcher.addEventListener('calculator.layout_loaded', function (layout) {
self.renderButtons(layout.buttonMap);
self.changeTitle(layout.name);
self.clearRearScreen();
});
// a button has been disabled
this.dispatcher.addEventListener('calculator.button_disabled', function (event) {
var buttonEl = self.buttons.get(event.slot);
if (buttonEl) {
buttonEl.disabled = true;
}
});
// a button has been enabled
this.dispatcher.addEventListener('calculator.button_enabled', function (slot) {
var buttonEl = self.buttons.get(slot);
if (buttonEl) {
buttonEl.disabled = false;
}
});
// the editor is shown/hidden
this.dispatcher.addEventListener('editor.show', this.hideRearScreen.bind(this));
this.dispatcher.addEventListener('editor.hide', this.showRearScreen.bind(this));
// a button has been selected
this.dispatcher.addEventListener('editor.button_selected', function (event) {
var newButtonEl = self.buttons.get(event.slot),
oldButtonEl = self.buttons.get(event.previousSlot);
if (oldButtonEl) {
oldButtonEl.parentElement.classList.remove('edit');
}
if (newButtonEl) {
newButtonEl.parentElement.classList.add('edit');
}
});
// a button has been renamed
this.dispatcher.addEventListener('editor.button_renamed', function (event) {
var buttonEl = self.buttons.get(event.slot),
text = self.texts.get(event.button.text),
hasEditClass;
if (buttonEl) {
buttonEl.innerText = text.button;
hasEditClass = buttonEl.parentElement.classList.contains('edit');
buttonEl.parentElement.className = text.type;
if (hasEditClass) {
buttonEl.parentElement.classList.add('edit');
}
}
});
},
_clearElement: function (element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
},
getRootElement: function () {
return document.getElementById(this.id);
},
render: function () {
var root = this.getRootElement();
this._clearElement(root);
if (!root.classList.contains('calculator')) {
root.classList.add('calculator');
}
this.createBaseMarkup(root);
this.rearScreen = document.createElement('ul');
root
.getElementsByClassName('rear-screen')
.item(0)
.appendChild(this.rearScreen);
this.frontScreen = document.createElement('ul');
root
.getElementsByClassName('front-screen')
.item(0)
.appendChild(this.frontScreen);
this.expressionRow = document.createElement('li');
this.expressionRow.classList.add('expression-row');
this.frontScreen.appendChild(this.expressionRow);
this.flagRow = document.createElement('li');
this.flagRow.classList.add('flag-row');
this.frontScreen.appendChild(this.flagRow);
this.resultRow = document.createElement('li');
this.resultRow.classList.add('result-row');
this.frontScreen.appendChild(this.resultRow);
this.rendered = true;
},
renderButtons: function (buttonMap) {
var buttonsTrs,
map = {},
buttons = document.createDocumentFragment(),
rootElement = this
.getRootElement()
.getElementsByClassName('screen')
.item(0)
.parentElement;
// map copy in order to prevent alteration of the original map
for (var slot in buttonMap) {
map[slot] = buttonMap[slot];
}
// adding constant non-editable buttons
map.a0 = Sankore.Button.create('s', 'del', -1, false);
map.b0 = Sankore.Button.create('l', 'left', -1, false);
map.c0 = Sankore.Button.create('r', 'right', -1, false);
map.d0 = Sankore.Button.create('c', 'clear', -1, false);
buttonsTrs = rootElement.getElementsByClassName('buttons');
while (buttonsTrs.length > 0) {
rootElement.removeChild(buttonsTrs[0]);
}
buttonsTrs = this.createButtons(map);
// we use a DocumentFragment to avoid excessive and useless repaints
for (var i in buttonsTrs) {
buttons.appendChild(buttonsTrs[i]);
}
rootElement.appendChild(buttons);
},
createBaseMarkup: function (element) {
var table = document.createElement('table'),
tr = document.createElement('tr'),
rearTd = document.createElement('td'),
frontTd = document.createElement('td');
rearTd.className = 'rear-screen';
rearTd.setAttribute('rowspan', '8');
tr.appendChild(rearTd);
frontTd.className = 'front-screen';
frontTd.setAttribute('colspan', '4');
tr.appendChild(frontTd);
tr.className = 'screen';
table.appendChild(tr);
element.appendChild(this.createTitle());
element.appendChild(this.createControls());
element.appendChild(table);
},
createTitle: function () {
this.title = document.createElement('span');
this.title.appendChild(document.createTextNode('Chargement...'));
this.title.className = 'title';
return this.title;
},
createControls: function () {
var controlsDiv = document.createElement('div'),
editorButton = document.createElement('button'),
resetButton = document.createElement('button'),
self = this;
if (this.withEditor) {
editorButton.setAttribute('type', 'button');
editorButton.appendChild(document.createTextNode(_('controls.editor')));
editorButton.addEventListener('click', function (e) {
self.dispatcher.notify('main_interface.editor_click');
});
controlsDiv.appendChild(editorButton);
}
resetButton.setAttribute('type', 'button');
resetButton.appendChild(document.createTextNode(_('controls.reset')));
resetButton.addEventListener('click', function (e) {
self.dispatcher.notify('main_interface.reset_click');
});
controlsDiv.appendChild(resetButton);
controlsDiv.className = 'controls';
return controlsDiv;
},
createButtons: function (buttonMap) {
var trs = [],
i;
for (i = 0; i < 7; i++) {
trs.push(this.createButtonRow(i, buttonMap));
}
return trs;
},
createButtonRow: function (rowNumber, buttonMap) {
var tr = document.createElement('tr'),
i;
tr.className = 'buttons';
for (i = 'a'; i <= 'd'; i = String.fromCharCode(i.charCodeAt(0) + 1)) {
tr.appendChild(this.createButton(i + rowNumber, buttonMap));
}
return tr;
},
createButton: function (slot, buttonMap) {
var td = document.createElement('td'),
self = this,
text,
buttonEl,
button = buttonMap[slot];
td.setAttribute('data-slot', slot);
td.setAttribute('data-editable', button.isEditable());
if (typeof button !== 'undefined') {
buttonEl = document.createElement('button');
text = this.texts.get(button.text);
buttonEl.innerText = text.button;
if (text.type !== 'normal') {
td.classList.add(text.type);
}
buttonEl.addEventListener('mousedown', function (e) {
self.handleButtonClick(this, e, button, slot);
});
this.buttons.set(slot, buttonEl);
this.dispatcher.notify('main_interface.button_created', {
button: button,
slot: slot
});
td.appendChild(buttonEl);
}
return td;
},
handleButtonClick: function (el, event, button, slot) {
this.dispatcher.notify('main_interface.button_click', {
button: button,
slot: slot
});
},
updateFlagRow: function () {
var span;
this._clearElement(this.flagRow);
for (var i in this.flags) {
span = document.createElement('span');
span.appendChild(document.createTextNode(this.flags[i]));
this.flagRow.appendChild(span);
}
},
updateExpressionRow: function (keystrokeText) {
this._clearElement(this.expressionRow);
this.expressionRow.appendChild(document.createTextNode(keystrokeText[0]));
this.expressionRow.appendChild(this.caret);
this.expressionRow.appendChild(document.createTextNode(keystrokeText[1]));
},
updateResultRow: function (result) {
this._clearElement(this.resultRow);
this.resultRow.appendChild(this.createResultRow(result));
},
createResultRow: function (result) {
try {
if (Sankore.Calculus.EuclideanDivisionOperation.isPrototypeOf(result)) {
return this.createResultRowEuclidean(result.getValue(), result.getRemainder());
} else if (Sankore.Calculus.Expression.isPrototypeOf(result)) {
return this.createResultRowExpression(result.getValue());
} else if (Sankore.Util.Error.isPrototypeOf(result)) {
return this.createResultRowError(result);
} else {
return this.createResultRowText(String(result));
}
} catch (e) {
return this.createResultRowError(e);
}
return null;
},
createResultRowEuclidean: function (quotient, remainder) {
var p = document.createElement('p'),
quotientSpan = document.createElement('span'),
remainderSpan = document.createElement('span');
p.classList.add('euclidean');
remainderSpan.classList.add('remainder');
remainderSpan.textContent = this.formatResultValue(remainder);
p.appendChild(remainderSpan);
quotientSpan.classList.add('quotient');
quotientSpan.textContent = this.formatResultValue(quotient);
p.appendChild(quotientSpan);
return p;
},
createResultRowExpression: function (value) {
return document.createTextNode(this.formatResultValue(value));
},
createResultRowText: function (text) {
return document.createTextNode(text);
},
createResultRowError: function (error) {
var errorDiv = document.createElement('div'),
text = _('error.common');
if (error.name === 'ZeroDivision') {
text = _('error.zero_division');
}
errorDiv.classList.add('error');
errorDiv.appendChild(document.createTextNode(text));
return errorDiv;
},
formatResultValue: function (raw) {
var fixed = raw.toFixed(Math.max(0, 10 - raw.toFixed().length)),
last;
if (fixed.indexOf('.') !== -1) {
while (true) {
last = fixed.charAt(fixed.length - 1);
if (last === "0") {
fixed = fixed.slice(0, -1);
continue;
}
if (last === '.') {
fixed = fixed.slice(0, -1);
}
break;
}
}
// si le nombre de chiffres dépassent 10, on tronque à 10 à droite
// et on arrondit au dernier (wtf)
if (Number(fixed) > (1e10 - 1)) {
fixed = Number(fixed.substring(0, 11) / 10).toFixed();
}
if (fixed.indexOf('.') !== -1) {
fixed = fixed.replace('.', _('text.comma'));
}
return fixed;
},
updateRearScreen: function (history) {
var expressionRow,
resultRow,
rows = document.createDocumentFragment();
for (var i in history) {
expressionRow = document.createElement('li');
expressionRow.className = 'expression-row';
expressionRow.appendChild(document.createTextNode(history[i].expression));
rows.appendChild(expressionRow);
resultRow = document.createElement('li');
resultRow.className = 'result-row';
resultRow.appendChild(this.createResultRow(history[i].output));
rows.appendChild(resultRow);
}
this.clearRearScreen();
this.rearScreen.appendChild(rows);
this.rearScreen.lastChild.scrollIntoView();
},
clearRearScreen: function () {
this._clearElement(this.rearScreen);
},
showRearScreen: function () {
this.rearScreen.style.display = 'block';
this.showTitle();
},
hideRearScreen: function () {
this.rearScreen.style.display = 'none';
this.hideTitle();
},
addFlag: function (flag) {
if (this.flags.indexOf(flag) === -1) {
this.flags.push(flag);
this.flags.sort();
}
},
removeFlag: function (flag) {
var idx = this.flags.indexOf(flag);
if (idx !== -1) {
this.flags.splice(idx, 1);
this.flags.sort();
}
},
changeTitle: function (title) {
this._clearElement(this.title);
this.title.appendChild(document.createTextNode(title));
},
showTitle: function () {
this.title.style.visibility = 'visible';
},
hideTitle: function () {
this.title.style.visibility = 'hidden';
}
}));
})();

@ -0,0 +1,15 @@
/*globals klass:true Sankore:true*/
(function(){
"use strict";
klass.define('Sankore.Util', 'Error', klass.extend({
constructor: function (name, message) {
this.name = name;
this.message = message;
},
toString: function () {
return this.name + ': ' + this.message;
}
}));
})();

@ -0,0 +1,48 @@
/*globals klass: true, Sankore:true*/
(function () {
"use strict";
klass.define('Sankore.Util', 'EventDispatcher', klass.extend({
constructor: function () {
this.eventListeners = {};
},
addEventSubscriber: function(subscriber) {
for (var i in subscriber.events) {
this.addEventListener(subscriber.events[i], subscriber.listener);
}
return this;
},
addEventListener: function (event, closure, id) {
if (typeof this.eventListeners[event] === 'undefined') {
this.eventListeners[event] = [];
}
if (typeof id === 'undefined') {
this.eventListeners[event].push(closure);
} else {
this.eventListeners[event][id] = closure;
}
return this;
},
removeEventListener: function (event, id) {
delete this.eventListeners[event][id];
},
removeAllEventListeners: function (event) {
this.eventListeners[event] = [];
},
notify: function (event, obj) {
var closure;
for (closure in this.eventListeners[event]) {
this.eventListeners[event][closure](obj);
}
}
}));
})();

@ -0,0 +1,78 @@
/*globals klass: true, Sankore: true*/
(function() {
"use strict";
klass.define('Sankore.Util', 'Hash', klass.extend({
constructor: function (elements) {
this.elements = elements || {};
},
length: function () {
return this.keys().length;
},
keys: function () {
return Object.keys(this.elements);
},
set: function (id, value) {
this.elements[id] = value;
},
add: function (id, values) {
for (var i in values) {
this.set(values[i][id], values[i]);
}
},
has: function (id) {
return this.keys().indexOf(id) !== -1;
},
get: function (id, def) {
if (typeof this.elements[id] !== 'undefined') {
return this.elements[id];
}
if (typeof def !== 'undefined') {
return def;
}
return null;
},
pos: function (id) {
var pos = 0;
for (var i in this.elements) {
if (this.elements.hasOwnProperty(i) && i === id) {
return pos;
}
pos++;
}
return null;
},
remove: function (id) {
return delete this.elements[id];
},
map: function (closure) {
var output = [],
called;
for (var id in this.elements) {
if (this.elements.hasOwnProperty(id)) {
called = closure.call(this, id, this.elements[id]);
if (called) {
output.push(called);
}
}
}
return output;
}
}));
})();

@ -0,0 +1,45 @@
/*jshint devel:true, browser:true*/
/*globals klass:true, Sankore:true*/
(function () {
"use strict";
klass.define('Sankore.Util', 'I18N', klass.extend({
catalogs: Sankore.Util.Hash.create(),
constructor: function () {
this.catalog = {};
},
load: function (locale) {
var localeId = locale.split(/-|_/)[0].toLowerCase();
if (!Sankore.Util.I18N.catalogs.has(localeId)) {
localeId = 'en';
}
this.catalog = Sankore.Util.I18N.catalogs.get(localeId, {});
},
translate: function (id) {
return id.split('.').reduce(
function (root, id) {
if (root && id in root) {
return root[id];
}
return null;
},
this.catalog
) || id;
}
}));
// global instance
Sankore.Util.i18n = Sankore.Util.I18N.create();
// global helper
window._ = function (id) {
return Sankore.Util.i18n.translate(id);
};
})();

@ -0,0 +1,74 @@
(function() {
Sankore.Util.I18N.catalogs.set('en', {
layout: {
classic_name: 'Basic calculator',
new_name: 'New calculator'
},
text: {
del: 'DEL',
comma: '.'
},
command: {
zero: '0 digit',
one: '1 digit',
two: '2 digit',
three: '3 digit',
four: '4 digit',
five: '5 digit',
six: '6 digit',
seven: '7 digit',
eight: '8 digit',
nine: '9 digit',
plus: 'Addition',
minus: 'Subtraction',
times: 'Multiplication',
divide: 'Division',
euclidean_divide: 'Euclidean division',
equal: 'Equal',
comma: 'Comma',
open_parenthesis: 'Open parenthesis',
close_parenthesis: 'Close parenthesis',
op: 'Constant operator',
memory_add: 'Memory addition',
memory_sub: 'Memory substraction',
memory_recall: 'Memory recall',
memory_clear: 'Memory clear',
clear: 'Clear',
left: 'Move left',
right: 'Move right',
del: 'Delete'
},
controls: {
editor: 'Editor',
reset: 'RST'
},
editor: {
run_button: 'Run',
remove_alert: 'Delete this calculator?',
layout_name: {
label: 'Name'
},
layout_description: {
label: 'Description'
},
assignation: {
enabled: 'Click on a button to edit it',
disabled: 'This calculator is not editable',
text: {
label: 'Display text'
},
command: {
label: 'Command'
},
use_limit: {
label: 'Use limit',
help: '0 for disabled, empty for unlimited'
}
}
},
error: {
common: 'Error',
zero_division: 'Div/0 error'
}
});
})();

@ -0,0 +1,74 @@
(function() {
Sankore.Util.I18N.catalogs.set('fr', {
layout: {
classic_name: 'Calculatrice standard',
new_name: 'Nouvelle calculatrice'
},
text: {
del: 'EFF',
comma: ','
},
command: {
zero: 'Chiffre 0',
one: 'Chiffre 1',
two: 'Chiffre 2',
three: 'Chiffre 3',
four: 'Chiffre 4',
five: 'Chiffre 5',
six: 'Chiffre 6',
seven: 'Chiffre 7',
eight: 'Chiffre 8',
nine: 'Chiffre 9',
plus: 'Addition',
minus: 'Soustraction',
times: 'Multiplication',
divide: 'Division',
euclidean_divide: 'Division euclidienne',
equal: 'Egalité',
comma: 'Virgule',
open_parenthesis: 'Parenthèse ouvrante',
close_parenthesis: 'Parenthèse fermante',
op: 'Opérateur constant',
memory_add: 'Addition mémoire',
memory_sub: 'Soustraction mémoire',
memory_recall: 'Rappel mémoire',
memory_clear: 'R.A.Z. mémoire',
clear: 'R.A.Z.',
left: 'Déplacement à gauche',
right: 'Déplacement à droite',
del: 'Suppression'
},
controls: {
editor: 'Editeur',
reset: 'RAZ'
},
editor: {
run_button: 'Utiliser',
remove_alert: 'Supprimer cette calculatrice ?',
layout_name: {
label: 'Nom'
},
layout_description: {
label: 'Description'
},
assignation: {
enabled: 'Cliquez sur une touche pour la modifier',
disabled: 'Cette calculatrice n\'est pas modifiable',
text: {
label: 'Texte à afficher'
},
command: {
label: 'Commande'
},
use_limit: {
label: 'Nb d\'utilisation',
help: '0 pour désactiver, vide pour illimité'
}
}
},
error: {
common: 'Erreur',
zero_division: 'Erreur div/0'
}
});
})();

@ -0,0 +1,78 @@
/*global window:true*/
(function () {
"use strict";
// polyfill
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis ? this : oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
window.klass = {
create: function () {
var self = Object.create(this);
if (typeof self.constructor === "function") {
self.constructor.apply(self, arguments);
}
return self;
},
extend: function (object) {
var self = Object.create(this);
if (!object) {
return self;
}
Object.keys(object).forEach(function (key) {
self[key] = object[key];
});
return self;
},
define: function (namespace, name, object) {
var createNamespace = function (namespace, root) {
var namespaceParts = namespace.split('.'),
first;
if (namespaceParts.length > 0) {
first = namespaceParts.shift();
if (typeof root[first] === 'undefined') {
root[first] = {};
}
if (namespaceParts.length > 0) {
return createNamespace(namespaceParts.join('.'), root[first]);
}
return root[first];
}
return null;
},
ns = createNamespace(namespace, window);
ns[name] = object;
}
};
})();

@ -0,0 +1,102 @@
* {
margin: 0;
padding: 0;
}
body{
margin:0px;
}
.ubw-container{
float:left;
margin:3px;
margin-right:0px;
margin-top: 2px;
background-image:url(../images/back_small.png);
overflow: hidden;
}
.ubw-body{
margin:5px;
margin-left: 9px;
margin-right: 0px;
}
.ubw-inspector{
position:absolute;
background-color:rgb(252, 252, 252);
border:1px solid #cccccc;
line-height:20px;
font-family:Arial, Helvetica, sans-serif;
font-weight:normal;
font-size:20px;
color:#333333;
}
.ubw-inpubox{
min-width:28px;
min-height:37px;
color:#333333;
background-image: url(../images/button_out.png);
border-left:1px solid rgb(231, 231, 231);
border-right:1px solid rgb(231, 231, 231);
border-bottom:1px solid rgb(221, 221, 221);
border-top:1px solid rgb(241, 241, 241);
}
/*BUTTONS*/
.ubw-button-wrapper{
float:left;
position:relative;
/*border:solid 1px yellow;*/
margin-right:-7px;
z-index:0;
font-family:Arial, Helvetica, sans-serif;
font-weight:normal;
font-size:30px;
overflow:visible;
}
.ubw-button-canvas{
width:auto;
float:left;
position:relative;
overflow:visible;
}
table{
line-height:90%;
}
.ubw-button-body{
position:relative;
float:left;
width:auto;
height:auto;
overflow:visible
text-align:center;
vertical-align:middle;
cursor:pointer;
}
.ubw-button-content{
height:auto;
width:auto;
text-align:center;
overflow:visible;
}
.ubw-button-over{
}
.ubw-button-out{
}
span.colored{
color: #0080ff;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save