Skip to content

Instantly share code, notes, and snippets.

@tomitrescak
Created February 6, 2017 04:52
Show Gist options
  • Save tomitrescak/75d9f158c48124431b3a3b989bb93f02 to your computer and use it in GitHub Desktop.
Save tomitrescak/75d9f158c48124431b3a3b989bb93f02 to your computer and use it in GitHub Desktop.
ts issue
interface IFileOwner {
files: App.Collections.ITextFileDAO[];
}
export const ClassUtils = {
alphanumSort<T>(array: Array<T>, selector?: (obj: T) => string, sensitivity = 'base') {
return array.sort((a, b) => selector(a).localeCompare(selector(b), undefined, {numeric: true, sensitivity}));
},
indexArray(arr: any[]): any[] {
if (arr.length === 0) {
return arr;
}
if (typeof arr[0] === 'string') {
let arr1 = <any>[];
for (let i = 0; i < arr.length; i++) {
arr1.push({ value: arr[i], index: i, nextIndex: i + 1 });
}
return arr1;
} else {
for (let i = 0; i < arr.length; i++) {
arr[i].index = i;
arr[i].nextIndex = i + 1;
}
}
return arr;
},
find<T>(obj: Object, callback: (elem: T) => boolean): T {
let props = Object.getOwnPropertyNames(obj);
for (let prop of props) {
if (callback(obj[prop])) {
return obj[prop];
}
}
return null;
},
/**
* Removes all comments from the code
*
* @param {string} code
* @returns {string}
*/
stripCode(code: string): string {
let formattedCode = '';
// remove strings
let sq = false, dq = false;
for (let c = 0; c < code.length; c++) {
if (code[c] === '\"' && code[c - 1] !== '\\') { dq = !sq && !dq; formattedCode += code[c]; continue; }
if (code[c] === "'" && code[c - 1] !== '\\') { sq = !dq && !sq; formattedCode += code[c]; continue; }
if (!dq && !sq) {
formattedCode += code[c];
}
}
// remove multi line comments
formattedCode = formattedCode.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\//g, '');
// remove single line comments
formattedCode = formattedCode.replace(/\/\/.*/g, '');
return formattedCode;
},
getLinesOfCodeInFiles(files: App.Entities.TextFile[]): App.Shared.ILocInfo {
let locInfo: App.Shared.ILocInfo = { loc: 0 };
for (let f of files) {
locInfo.loc += ClassUtils.getLinesOfCode(f.source).loc;
}
return locInfo;
},
getLinesOfCode(code: string): App.Shared.ILocInfo {
let locInfo: App.Shared.ILocInfo = {};
locInfo.loc = 0;
if (code === null) {
return locInfo;
}
const formattedCode = ClassUtils.stripCode(code);
let loc = 0;
// count functions
let fcReg = /(String|boolean|int|double|float|void|function)( +([\w_]+) *\()/g;
let fcMatches: RegExpExecArray = null;
locInfo.functions = 0;
while ((fcMatches = fcReg.exec(formattedCode)) !== null) {
locInfo.functions++;
const name = fcMatches[3];
let fcUseReg = new RegExp(name + ' *\\(', 'g');
if (formattedCode.match(fcUseReg).length > 1) { loc -= 1; };
}
// count semicolons
let matches = formattedCode.match(/;/g);
if (matches !== null) {
loc += matches.length;
locInfo.commands = matches.length;
}
// match all ifs
matches = formattedCode.match(/if\W*\(/g);
if (matches !== null) {
loc += matches.length;
locInfo.ifs = matches.length;
}
// match all elses
matches = formattedCode.match(/else\W*/g);
if (matches !== null) {
loc += matches.length;
locInfo.elses = matches.length;
}
// deduct all else ifs
matches = formattedCode.match(/else\W+if\W*\(/g);
if (matches !== null) {
loc -= matches.length;
locInfo.elseIfs = matches.length;
}
// match all whiles
matches = formattedCode.match(/while\W*\(/g);
if (matches !== null) {
loc += matches.length;
locInfo.whiles = matches.length;
}
// match all dos
matches = formattedCode.match(/do\W*\{/g);
if (matches !== null) {
loc += matches.length;
locInfo.dos = matches.length;
}
// match all fors (we put minus, since each for has two semicolons)
matches = formattedCode.match(/for\W*\(/g);
if (matches !== null) {
locInfo.fors = matches.length;
}
// match all fors (we put minus, since each for has two semicolons)
matches = formattedCode.match(/\bcase\b/g);
if (matches !== null) {
loc += matches.length;
locInfo.cases = matches.length;
}
// match all fors (we put minus, since each for has two semicolons)
matches = formattedCode.match(/\bswitch\b/g);
if (matches !== null) {
loc += matches.length;
locInfo.switches = matches.length;
}
// match all logical expressions
matches = formattedCode.match(/(\|\||\&\&)/g);
if (matches !== null) {
locInfo.logicals = matches.length;
loc += matches.length;
}
locInfo.loc = loc;
return locInfo;
},
createFormObject(obj: any) {
const result = {} as any;
const keys = Object.getOwnPropertyNames(obj);
// prepare owner
for (let key of keys) {
let owner = result;
const parts = key.split('.');
for (let i = 0; i < parts.length - 1; i++) {
const part = parts[i];
const isArray = Number.isInteger(parseInt(parts[i + 1], 10));
// if owner does not have this part
if (!owner[part]) {
// if next part is integer we will create an array
if (isArray) {
let newOwner = [] as any;
owner[part] = newOwner;
owner = newOwner;
} else {
let newOwner = {};
owner[part] = newOwner;
owner = newOwner;
}
} else {
owner = owner[part];
}
}
const lastKey = parts[parts.length - 1];
const isArray = Number.isInteger(parseInt(lastKey, 10));
if (isArray) {
owner.push(obj[key]);
} else {
owner[lastKey] = obj[key];
}
}
return result;
},
mergeFiles(owners: IFileOwner[], keepCopies = false) {
let files: App.Collections.ITextFileDAO[] = [];
let ownerIndex = 0;
owners = owners.reverse();
for (let owner of owners) {
if (!owner || !owner.files) {
continue;
}
for (let file of owner.files.reverse()) {
// find and push or replace the element
const index = files.findIndex((f) => f.name === file.name);
if (index === -1) {
files.unshift(file);
} else {
if (keepCopies && !files[index].copyOf) {
files[index].copyOf = file;
}
}
// for all non-owner files we mark them readonly
if (ownerIndex > 0) {
file.readonly = true;
}
}
ownerIndex ++;
}
return files;
}
};
// extending prototype
String.prototype.toUrlName = function (this: string) {
let result = this.replace(/\:/g, '');
result = result.replace(/ - /g, '-');
result = result.replace(/\W/g, '-');
do {
result = result.replace(/--/g, '-');
} while (result.indexOf('--') >= 0);
return result.toLowerCase();
};
String.prototype['safeFilePath'] = function (this: string) {
return this.replace(/\.\./g, '');
};
export default ClassUtils;
import Config from './config';
import React from 'react';
import Utils from './utils';
import getState from './state';
// import * as Caches from './caches';
// import { query, mutation } from 'apollo-mantra';
// import { mutationWithFeedback, addInsertData, addModificationData } from '../helpers/apollo_helpers';
// const Apollo = {
// query,
// mutation,
// mutationWithFeedback,
// addModificationData,
// addInsertData
// };
let context: App.Context;
let state = getState();
export function initContext(forceInit = false, user?: App.User) {
if (!context || forceInit) {
context = {
state: getState(forceInit, user),
// mobxStores: {
// auth: state.accounts
// },
Utils,
Config
};
}
return context;
}
// context provider
interface Props {
context?: App.Context;
}
export class ContextProvider extends React.Component<Props, {}> {
getChildContext() {
return this.props.context || initContext();
}
render(): any {
return this.props.children;
}
}
export const contextTypes = {
state: React.PropTypes.object,
auth: React.PropTypes.object,
Utils: React.PropTypes.object,
Config: React.PropTypes.object,
Binder: React.PropTypes.object,
client: React.PropTypes.object,
store: React.PropTypes.any,
router: React.PropTypes.any
};
ContextProvider['childContextTypes'] = contextTypes;
// export const ContextProvider = ({ children }: any) => (
// <Provider state={state} auth={state.accounts} Utils={Utils} Config={Config}>
// { children }
// </Provider>
// );
// global type definitions
declare global {
namespace App {
export interface Dictionary<V> {
[key: string]: V;
}
export interface Context {
state: typeof state;
// mobxStores: {
// auth: typeof state.accounts;
// };
Utils: typeof Utils;
Config: typeof Config;
client?: any;
store?: any;
router?: any;
}
}
}
export default initContext;
import { observable } from 'mobx';
import User, { profileData } from '../modules/user/models/user_model';
import { AccountState, initState } from 'apollo-authentication-semantic-ui';
import modalState from '../modules/core/actions/modal_state';
import Configuration from '../modules/configuration/models/configuration_model';
import File from '../modules/text_editor/models/text_file_model';
import Schedule from '../modules/schedules/models/schedule_model';
import Practical from '../modules/practicals/models/practical_model';
import Exercise from '../modules/exercises/models/exercise_model';
import Marking from '../modules/marking/models/marking_model';
import Solution from '../modules/exercises/models/solution_model';
import Organisation from '../modules/organisations/models/organisation_model';
import World from '../modules/worlds/models/world_model';
import Debugger from '../modules/debugger/models/debugger_model';
import TextFile from '../modules/text_editor/models/text_file_model';
import { Profile } from '../modules/user/models/profile_state_model';
import ClassHelpers from '../../shared/helpers/class_helpers';
import { RouterUtils } from '../helpers/helpers_client';
// import scheduleReducer, { IScheduleState } from '../modules/schedules/actions/schedule_reducer';
// import practicalReducer, { IPracticalState } from '../modules/practicals/actions/practical_reducer';
// import exerciseReducer, { ExerciseState } from '../modules/exercises/actions/exercise_reducer';
// import solutionReducer, { ISolutionState } from '../modules/exercises/actions/solution_reducer';
import compilerState from '../modules/exercises/models/compiler_state';
// import worldReducer, { IWorldState } from '../modules/worlds/actions/worlds_reducer';
import editorState from '../modules/text_editor/models/text_editor_state';
// import boardReducer, { IBoardState } from '../modules/board/actions/board_reducer';
// import debuggerReducer, { IDebuggerState } from '../modules/debugger/actions/debugger_reducer';
class StateModel implements App.State {
@observable activeOrganisation = 'public';
@observable saving = false;
@observable activeTool = 'edit';
accounts: AccountState<App.User>;
compiler = compilerState;
editor = editorState;
modal = modalState;
@observable feedbackSubmitted = false;
schedules = {};
practicals = {};
exercises = {};
exerciseEntities = {};
solutions = {};
boardEditors = {};
worlds = {};
debuggers = {};
marking = {};
organisations = {};
configuration: App.Models.Configuration = null;
profile: Profile = null;
constructor(user?: App.User) {
this.activeOrganisation = user ? user.profile.organisations[0] : 'public';
const state = new AccountState((data: any) => new User(data, this.login.bind(this), this.logout.bind(this)), profileData, user);
this.accounts = state;
initState(state);
}
login(data: App.User) {
this.activeOrganisation = data.profile.organisations[0];
}
logout() {
this.activeOrganisation = 'public';
this.profile = null;
RouterUtils.go('/');
}
getModel(id: string, collection: Object, data: any, Model: any) {
let model = collection[id];
if (!model && data && data._id === id) {
model = new Model(data);
collection[id] = model;
}
return model;
}
getConfiguration(data: App.Entities.Configuration) {
if (this.configuration === null) {
this.configuration = new Configuration(data);
}
return this.configuration;
}
getEntity(id: string, collection: Object, data: any) {
let model = collection[id];
if (!model && data) {
collection[id] = model;
}
return model;
}
getModelWithContructor(id: string, collection: Object, data: any, ctor: Function) {
let model = collection[id];
if (!model && data) {
model = ctor();
collection[id] = model;
}
return model;
}
getSchedule(id: string, data: any): Schedule {
return this.getModel(id, this.schedules, data, Schedule);
}
getPractical(id: string, data: any): Practical {
return this.getModel(id, this.practicals, data, Practical);
}
getExercise(id: string, world: App.Models.World, data: App.Entities.Exercise): Exercise {
let exercise = this.exercises[id];
if (!exercise && data) {
// we need to init files in case we are creating a fresh version
exercise = data;
const tempExercise = {
files: data.files ? data.files.map(s => new TextFile(s)) : []
};
const mergedFiles = ClassHelpers.mergeFiles([world, tempExercise], true).map(f => new File(f));
const newExercise = Object.assign({}, data, { files: mergedFiles });
exercise = new Exercise(world, newExercise);
this.exercises[id] = exercise;
}
return exercise;
}
getExerciseEntity(id: string, data?: any): Practical {
return this.getEntity(id, this.exerciseEntities, data);
}
getOrganisation(id: string, data: any): App.Models.Organisation {
return this.getModel(id, this.organisations, data, Organisation);
}
getDebugger(id: string) {
if (!this.solutions[id]) {
throw new Error('Solution does not exist!: ' + id);
}
if (!this.debuggers[id]) {
this.debuggers[id] = new Debugger(this.solutions[id], this.compiler.getCompiler(id), this.editor.getEditor(id));
}
return this.debuggers[id];
}
hasSolution(id: string): boolean {
return this.solutions[id] != null;
}
getSolution(id: string, data?: any, schedule?: App.Entities.Schedule, practical?: App.Entities.Practical, exercise?: App.Entities.Exercise, world?: App.Models.World): Solution {
let solution = this.solutions[id];
if (!solution && data) {
solution = new Solution(data);
solution.init(schedule, practical, exercise, world);
this.solutions[id] = solution;
// init files, we need to init TextFile objects as original objects are not extensible
const tempExercise = {
files: exercise.files ? exercise.files.map(s => new TextFile(s)) : []
};
const tempSolution = {
files: solution.files ? solution.files.map(s => new TextFile(s)) : []
};
const mergedFiles = ClassHelpers.mergeFiles([world, tempExercise, tempSolution]).map(f => new File(f));
solution.files = observable(mergedFiles);
}
return solution;
}
getWorld(id: string, data?: any): World {
return this.getModel(id, this.worlds, data, World);
}
getWorlds(worlds: App.Entities.World[]): { [index: string]: World } {
if (Object.keys(this.worlds).length === 0) {
worlds.forEach(w => this.worlds[w._id] = new World(w));
};
return this.worlds;
}
getMarkingState(id: string): Marking {
if (!this.marking[id]) {
this.marking[id] = new Marking();
}
return this.marking[id];
}
getProfile() {
if (!this.accounts.userId) {
this.profile = null;
return null;
}
if (!this.profile) {
this.profile = new Profile(this.accounts.user);
}
return this.profile;
}
}
declare global {
namespace App {
type State = StateModel;
}
}
let state: StateModel;
export default function initAppState(forceInit = false, user?: App.User): App.State {
if (!state || forceInit) {
state = new StateModel(user);
global['__state'] = state;
}
return state;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment