Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
typo3
qfq
Commits
17341efd
Commit
17341efd
authored
Sep 15, 2018
by
Carsten Rose
Browse files
Merge branch 'thesisCodeCorrection' into 'master'
Thesis code correction See merge request
!71
parents
989ff133
1a4bae6c
Pipeline
#889
passed with stage
in 1 minute and 49 seconds
Changes
9
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
javascript/src/CodeCorrection.js
0 → 100644
View file @
17341efd
/**
* @author Benjamin Baer <benjamin.baer@math.uzh.ch>
*/
/* global $ */
/* global EventEmitter */
/* @depend QfqEvents.js */
/* @depend Alert.js */
/* @depend Comment.js */
/* @depend CommentController */
/* @depend SyntaxHighlighter */
/**
* Qfq Namespace
*
* @namespace QfqNS
*/
var
QfqNS
=
QfqNS
||
{};
(
function
(
n
)
{
'
use strict
'
;
/**
* Displays Code in a stylized fashion and allows
* to write and display comments for each line of code.
*
* @param form Reference to the parent qfq Element
* @param data Object containing the to be displayed data
* @param $container Reference to the HTML Element that displays
* the code correction
* @param $target Reference to the HTML Element where the output (comment JSON)
* should be stored.
*/
n
.
CodeCorrection
=
function
(
form
,
data
,
$container
,
$target
,
currentUser
,
language
)
{
this
.
form
=
form
;
this
.
data
=
data
;
this
.
eventEmitter
=
new
EventEmitter
();
this
.
$parent
=
$container
;
this
.
$target
=
$target
;
this
.
$rows
=
[];
this
.
annotations
=
[];
this
.
users
=
[];
this
.
currentUser
=
currentUser
;
this
.
language
=
language
;
this
.
syntaxHighlight
=
{};
};
/**
* Initializes the Code Correction object, fetches data from URL or API if needed
*/
n
.
CodeCorrection
.
prototype
.
initialize
=
function
()
{
var
that
=
this
;
if
(
this
.
$target
.
val
())
{
var
jImport
=
$
.
parseJSON
(
this
.
$target
.
val
());
if
(
jImport
.
annotations
)
{
this
.
annotations
=
jImport
.
annotations
;
console
.
log
(
"
[CodeCorrection] Imported Annotations:
"
+
this
.
annotations
.
length
);
}
if
(
jImport
.
users
)
{
this
.
users
=
jImport
.
users
;
console
.
log
(
"
[CodeCorrection] Imported Users:
"
+
this
.
users
.
length
);
}
}
if
(
this
.
data
.
url
)
{
// Get data of a file and write it to data.text
$
.
get
(
this
.
data
.
url
,
function
(
response
)
{
that
.
data
.
text
=
response
;
that
.
_prepareBuild
();
});
}
else
if
(
this
.
data
.
text
)
{
this
.
_prepareBuild
();
}
else
{
console
.
error
(
"
[CodeCorrection] No Code to correct passed to the object.
"
);
}
};
n
.
CodeCorrection
.
prototype
.
_prepareBuild
=
function
()
{
var
that
=
this
;
this
.
syntaxHighlight
=
new
n
.
SyntaxHighlighter
();
this
.
syntaxHighlight
.
importInstructions
(
this
.
language
,
function
()
{
that
.
_buildEditor
();
});
};
/**
* Breaks up the String by line and returns it as an Array
* @param text Unix formatted text of the Code File
* @returns {Array} Array with the Code broken up by Line
*/
n
.
CodeCorrection
.
prototype
.
createLineByLineArray
=
function
(
text
)
{
var
textArray
=
[];
if
(
typeof
text
===
'
string
'
||
text
instanceof
String
)
{
textArray
=
text
.
split
(
"
\n
"
);
}
return
textArray
;
};
/**
* Builds the Code Correction HTML Element that should be displayed to the user.
* @private
*/
n
.
CodeCorrection
.
prototype
.
_buildEditor
=
function
()
{
var
that
=
this
;
var
$title
=
$
(
'
<div/>
'
,
{
class
:
'
qfqCodeCorrectionTitle
'
});
$title
.
appendTo
(
this
.
$parent
);
var
container
=
$
(
'
<div/>
'
,
{
class
:
'
codeCorrectionWrap
'
});
var
lineCount
=
1
;
var
textArray
=
this
.
createLineByLineArray
(
this
.
data
.
text
);
textArray
.
forEach
(
function
(
line
)
{
that
.
$rows
[
lineCount
]
=
that
.
_buildLine
(
lineCount
,
line
);
that
.
$rows
[
lineCount
].
appendTo
(
container
);
lineCount
++
;
});
container
.
appendTo
(
this
.
$parent
);
this
.
_buildAnnotations
();
};
/**
* Checks which codelines have annotations and initializes a CommentController
* for each of them.
* @private
*/
n
.
CodeCorrection
.
prototype
.
_buildAnnotations
=
function
()
{
for
(
var
i
=
0
;
i
<
this
.
annotations
.
length
;
i
++
)
{
var
annotation
=
this
.
annotations
[
i
];
var
$hook
=
this
.
$rows
[
annotation
.
lineNumber
];
var
commentController
=
this
.
_buildCommentContainer
(
$hook
);
commentController
.
importComments
(
annotation
.
comments
,
this
.
users
);
this
.
_setListeners
(
commentController
);
this
.
_setCommentController
(
annotation
.
lineNumber
,
commentController
);
}
};
/**
* Builds a Line as a combination of HTML Elements. Binds the necessary Events.
*
* @param lineCount
* @param line
* @returns {jQuery|HTMLElement}
* @private
*/
n
.
CodeCorrection
.
prototype
.
_buildLine
=
function
(
lineCount
,
line
)
{
var
that
=
this
;
var
htmlRow
=
$
(
'
<div/>
'
,
{
class
:
'
clearfix qfqCodeLine
'
,
id
:
'
qfqC
'
+
lineCount
});
htmlRow
.
on
(
"
click
"
,
function
()
{
that
.
_handleClick
(
htmlRow
,
lineCount
);});
var
htmlLineNumber
=
$
(
'
<div/>
'
,
{
class
:
'
pull-left qfqLineCount
'
,
text
:
lineCount
});
var
cLine
=
line
.
replace
(
/
\s
/g
,
'
'
)
.
replace
(
'
<
'
,
'
<
'
)
.
replace
(
'
>
'
,
'
>
'
);
cLine
=
this
.
syntaxHighlight
.
highlightLine
(
cLine
);
var
htmlCodeLine
=
$
(
'
<div/>
'
,
{
class
:
'
pull-right qfqCode
'
});
htmlCodeLine
.
html
(
cLine
);
htmlLineNumber
.
appendTo
(
htmlRow
);
htmlCodeLine
.
appendTo
(
htmlRow
);
return
htmlRow
;
};
/**
* Initializes a CommentContainer at a given jQuery Hook and returns
* the CommentContainer object
* @param $hook
* @returns {QfqNS.CommentController}
* @private
*/
n
.
CodeCorrection
.
prototype
.
_buildCommentContainer
=
function
(
$hook
)
{
var
commentController
=
new
n
.
CommentController
();
commentController
.
buildContainer
(
$hook
);
commentController
.
setCurrentUser
(
this
.
currentUser
);
return
commentController
;
};
/**
* References the CommentController in this.annotations Array
* for easy access later.
* @param lineCount
* @param commentController
* @returns {boolean}
* @private
*/
n
.
CodeCorrection
.
prototype
.
_setCommentController
=
function
(
lineCount
,
commentController
)
{
for
(
var
i
=
0
;
i
<
this
.
annotations
.
length
;
i
++
)
{
if
(
this
.
annotations
[
i
].
lineNumber
===
lineCount
)
{
this
.
annotations
[
i
].
commentController
=
commentController
;
return
true
;
}
}
return
false
;
};
/**
* Sets listeners for events generated by the CommentController object
* @param commentController
* @private
*/
n
.
CodeCorrection
.
prototype
.
_setListeners
=
function
(
commentController
)
{
var
that
=
this
;
commentController
.
on
(
'
comment.added
'
,
function
(
argument
)
{
console
.
log
(
"
Catch event:
"
+
that
.
annotations
.
length
);
console
.
log
(
"
With data:
"
+
argument
.
data
);
});
};
/**
* Places a comment editor under the hook.
* @param $hook
* @param lineCount
* @private
*/
n
.
CodeCorrection
.
prototype
.
_handleClick
=
function
(
$hook
,
lineCount
)
{
var
comments
=
{};
if
(
this
.
_hasComments
(
lineCount
))
{
comments
=
this
.
_getComments
(
lineCount
);
comments
.
commentController
.
toggle
();
comments
.
commentController
.
emitEvent
(
"
new
"
);
}
else
{
comments
.
lineNumber
=
lineCount
;
comments
.
commentController
=
new
n
.
CommentController
();
comments
.
commentController
.
buildContainer
(
$hook
);
comments
.
commentController
.
displayEditor
();
this
.
_setListeners
(
comments
.
commentController
);
this
.
annotations
.
push
(
comments
);
}
};
/**
* Checks if a line already has comments
* @param lineCount
* @returns {boolean}
* @private
*/
n
.
CodeCorrection
.
prototype
.
_hasComments
=
function
(
lineCount
)
{
for
(
var
i
=
0
;
i
<
this
.
annotations
.
length
;
i
++
)
{
if
(
this
.
annotations
[
i
].
lineNumber
===
lineCount
)
{
return
true
;
}
}
return
false
;
};
/**
* Gets comments for a specific line or returns false if said
* comments can't be found.
* @param lineCount
* @returns {*}
* @private
*/
n
.
CodeCorrection
.
prototype
.
_getComments
=
function
(
lineCount
)
{
for
(
var
i
=
0
;
i
<
this
.
annotations
.
length
;
i
++
)
{
if
(
this
.
annotations
[
i
].
lineNumber
===
lineCount
)
{
return
this
.
annotations
[
i
];
}
}
return
false
;
};
})(
QfqNS
);
\ No newline at end of file
javascript/src/Comment.js
0 → 100644
View file @
17341efd
/**
* @author Benjamin Baer <benjamin.baer@math.uzh.ch>
*/
/* global $ */
/* global EventEmitter */
/* @depend QfqEvents.js */
/* @depend Alert.js */
/**
* Qfq Namespace
*
* @namespace QfqNS
*/
var
QfqNS
=
QfqNS
||
{};
(
function
(
n
)
{
'
use strict
'
;
/**
* Displays Comment or an Editor to create a comment
*
* https://www.quora.com/What-is-the-best-way-to-check-if-a-property-or-variable-is-undefined
*
*/
n
.
Comment
=
function
(
comment
,
user
,
$container
,
options
)
{
this
.
comment
=
comment
;
this
.
user
=
user
;
this
.
$parent
=
$container
;
this
.
$comment
=
{};
if
(
arguments
.
length
==
3
)
{
this
.
options
=
{
readonly
:
false
};
}
else
{
this
.
options
=
options
;
}
this
.
childrenController
=
{};
};
n
.
Comment
.
prototype
.
display
=
function
()
{
var
displayElement
;
displayElement
=
this
.
_buildComment
();
displayElement
.
appendTo
(
this
.
$parent
);
this
.
$comment
=
displayElement
;
};
n
.
Comment
.
prototype
.
getParent
=
function
()
{
return
this
.
$parent
;
};
n
.
Comment
.
prototype
.
height
=
function
()
{
return
this
.
$comment
.
height
();
};
n
.
Comment
.
prototype
.
_buildComment
=
function
(
allowEdit
)
{
var
$commentWrap
=
$
(
'
<div/>
'
,
{
class
:
"
qfqComment
"
});
var
$avatar
=
$
(
'
<img>
'
,
{
src
:
this
.
user
.
avatar
,
class
:
"
qfqCommentAvatar
"
});
$avatar
.
appendTo
(
$commentWrap
);
var
$topLine
=
$
(
'
<div />
'
,
{
class
:
"
qfqCommentTopLine
"
});
$
(
'
<span />
'
,
{
class
:
"
qfqCommentAuthor
"
,
text
:
this
.
user
.
name
+
"
:
"
}).
appendTo
(
$topLine
);
$
(
'
<span />
'
,
{
class
:
"
qfqCommentDateTime
"
,
text
:
this
.
comment
.
dateTime
}).
appendTo
(
$topLine
);
$topLine
.
appendTo
(
$commentWrap
);
var
$comment
=
$
(
'
<div />
'
,
{
class
:
"
qfqCommentText
"
,
});
$comment
.
html
(
this
.
comment
.
comment
);
var
$commentCommands
=
$
(
"
<div />
"
,
{
class
:
"
qfqCommentCommands
"
});
var
$reply
=
$
(
'
<span />
'
,
{
class
:
"
glyphicon glyphicon-comment qfqCommentReply
"
});
$reply
.
appendTo
(
$commentCommands
);
$commentCommands
.
appendTo
(
$comment
);
$comment
.
appendTo
(
$commentWrap
);
return
$commentWrap
;
};
})(
QfqNS
);
\ No newline at end of file
javascript/src/CommentController.js
0 → 100644
View file @
17341efd
/**
* @author Benjamin Baer <benjamin.baer@math.uzh.ch>
*/
/* global $ */
/* global EventEmitter */
/* @depend QfqEvents.js */
/* @depend Alert.js */
/* @depend Comment.js */
/**
* Qfq Namespace
*
* @namespace QfqNS
*/
var
QfqNS
=
QfqNS
||
{};
(
function
(
n
)
{
'
use strict
'
;
/**
* Manages a group of comments
*
*/
n
.
CommentController
=
function
()
{
this
.
comments
=
[];
this
.
currentUser
=
{};
this
.
$container
=
{};
this
.
$parent
=
{};
this
.
height
=
"
auto
"
;
// Event Emitter is a Library qfq uses to emit custom Events.
this
.
eventEmitter
=
new
EventEmitter
();
};
n
.
CommentController
.
prototype
.
on
=
n
.
EventEmitter
.
onMixin
;
n
.
CommentController
.
prototype
.
setCurrentUser
=
function
(
user
)
{
this
.
currentUser
=
user
;
};
/**
* changeHandler emits custom events for actions.
* Additionally writes log entries to console for easier
* testing.
* @private
* @param event String containing possible change states
* @return {boolean} true on success
*/
n
.
CommentController
.
prototype
.
_changeHandler
=
function
(
event
)
{
if
(
event
===
"
edit
"
)
{
this
.
eventEmitter
.
emitEvent
(
'
comment.edited
'
,
n
.
EventEmitter
.
makePayload
(
this
,
"
edit
"
));
console
.
log
(
"
[CommentController] Event comment.edit emitted
"
);
return
true
;
}
else
if
(
event
===
"
new
"
)
{
this
.
eventEmitter
.
emitEvent
(
'
comment.added
'
,
n
.
EventEmitter
.
makePayload
(
this
,
"
add
"
));
console
.
log
(
"
[CommentController] Event comment.add emitted
"
);
return
true
;
}
else
if
(
event
===
"
remove
"
)
{
this
.
eventEmitter
.
emitEvent
(
'
comment.removed
'
,
n
.
EventEmitter
.
makePayload
(
this
,
"
remove
"
));
}
console
.
error
(
"
[CommentController] Changehandler called without valid event
"
);
return
false
;
};
n
.
CommentController
.
prototype
.
emitEvent
=
function
(
event
)
{
this
.
_changeHandler
(
event
);
};
n
.
CommentController
.
prototype
.
buildContainer
=
function
(
$hook
)
{
var
$container
=
$
(
"
<div />
"
,
{
class
:
"
qfqCommentContainer
"
});
$hook
.
after
(
$container
);
this
.
$container
=
$container
;
};
n
.
CommentController
.
prototype
.
hasComments
=
function
()
{
if
(
this
.
comments
.
length
>
0
)
{
return
true
;
}
else
{
return
false
;
}
};
n
.
CommentController
.
prototype
.
toggle
=
function
()
{
this
.
$container
.
slideToggle
(
"
swing
"
);
};
n
.
CommentController
.
prototype
.
getComment
=
function
(
reference
)
{
if
(
reference
<
this
.
comments
.
length
&&
reference
>=
0
)
{
return
this
.
comments
[
reference
];
}
else
{
console
.
error
(
"
[CommentController] Requested Comment doesn't exist
"
);
return
false
;
}
};
n
.
CommentController
.
prototype
.
addComment
=
function
(
comment
,
user
)
{
var
commentObject
=
new
n
.
Comment
(
comment
,
user
,
this
.
$container
);
commentObject
.
display
();
this
.
comments
.
push
(
commentObject
);
this
.
updateHeight
();
return
this
.
comments
.
length
-
1
;
};
n
.
CommentController
.
prototype
.
displayComments
=
function
()
{
for
(
var
i
=
0
;
this
.
comments
;
i
++
)
{
this
.
comments
[
i
].
display
();
}
this
.
updateHeight
();
};
n
.
CommentController
.
prototype
.
displayEditor
=
function
()
{
var
editor
=
new
n
.
Editor
();
var
that
=
this
;
var
$editor
=
editor
.
buildEditor
();
editor
.
on
(
"
editor.submit
"
,
function
(
editor
)
{
that
.
_handleEditorSubmit
(
editor
);
});
$editor
.
appendTo
(
this
.
$container
);
};
n
.
CommentController
.
prototype
.
_handleEditorSubmit
=
function
(
editor
)
{
var
comment
=
this
.
buildCommentObject
(
editor
.
data
.
text
);
this
.
addComment
(
comment
,
this
.
currentUser
);
editor
.
data
.
destroy
();
};
n
.
CommentController
.
prototype
.
buildCommentObject
=
function
(
text
)
{
var
comment
=
{};
comment
.
comment
=
text
;
comment
.
dateTime
=
new
Date
().
toISOString
();
comment
.
uid
=
this
.
currentUser
.
uid
;
return
comment
;
};
n
.
CommentController
.
prototype
.
getContainer
=
function
()
{
return
this
.
$container
;
};
n
.
CommentController
.
prototype
.
removeComment
=
function
(
reference
)
{
};
n
.
CommentController
.
prototype
.
updateComment
=
function
(
reference
,
data
)
{
};
n
.
CommentController
.
prototype
.
updateHeight
=
function
()
{
//this.height = this.$container.height();
//this.$container.css("max-height", this.height);
};
n
.
CommentController
.
prototype
.
importComments
=
function
(
comments
,
users
)
{
for
(
var
i
=
0
;
i
<
comments
.
length
;
i
++
)
{
var
user
=
this
.
_searchUsersByUid
(
users
,
comments
[
i
].
uid
);
this
.
addComment
(
comments
[
i
],
user
);
}
this
.
displayEditor
();
};
n
.
CommentController
.
prototype
.
_searchUsersByUid
=
function
(
users
,
uid
)
{
for
(
var
i
=
0
;
i
<
users
.
length
;
i
++
)
{
if
(
users
[
i
].
uid
===
uid
)
{
return
users
[
i
];
}
}
};
})(
QfqNS
);
\ No newline at end of file
javascript/src/Editor.js
0 → 100644
View file @
17341efd
/**
* @author Benjamin Baer <benjamin.baer@math.uzh.ch>
*/
/* global $ */
/* global EventEmitter */
/* @depend QfqEvents.js */
/* @depend Alert.js */
/* @depend Comment.js */
/**
* Qfq Namespace
*
* @namespace QfqNS
*/
var
QfqNS
=
QfqNS
||
{};
(
function
(
n
)
{
'
use strict
'
;
/**
* Manages Text Editor for Comments
*
*/
n
.
Editor
=
function
()
{
this
.
$container
=
{};
this
.
$textArea
=
{};
this
.
$submitButton
=
{};
this
.
text
=
""
;
// Event Emitter is a Library qfq uses to emit custom Events.
this
.
eventEmitter
=
new
EventEmitter
();
};
n
.
Editor
.
prototype
.
on
=
n
.
EventEmitter
.
onMixin
;
n
.
Editor
.
prototype
.
buildEditor
=
function
()
{
var
that
=
this
;
this
.
$container
=
$
(
"
<div />
"
,
{
class
:
"
qfqEditorContainer
"
});
this
.
$textArea
=
$
(
"
<div />
"
,
{
class
:
"
qfqEditor
"
,
contenteditable
:
true
});
this
.
$textArea
.
keydown
(
function
()
{
that
.
activateSubmit
();
});
this
.
_addEditorControls
();
var
controls
=
$
(
"
<div />
"
,
{
class
:
"
qfqEditorControls
"
});
var
submitButton
=
$
(
"
<button />
"
,
{
class
:
"
btn btn-primary
"
,
disabled
:
true
,
text
:
"
Send
"
});
submitButton
.
on
(
"
click
"
,
function
()
{
that
.
_handleClick
();});
submitButton
.
appendTo
(
controls
);
this
.
$submitButton
=
submitButton
;
this
.
$textArea
.
appendTo
(
this
.
$container
);
controls
.
appendTo
(
this
.
$container
);
return
this
.
$container
;
};
n
.
Editor
.
prototype
.
activateSubmit
=
function
()
{
this
.
$submitButton
.
attr
(
"
disabled
"
,
false
);
};