Browse Source

First comments version finalized.

pull/329/head
Sebastian Stehle 7 years ago
parent
commit
b73bc21145
  1. 1
      src/Squidex.Domain.Apps.Entities/Comments/CommentsGrain.cs
  2. 2
      src/Squidex.Domain.Apps.Events/Comments/CommentDeleted.cs
  3. 32
      src/Squidex/Areas/Api/Controllers/Comments/CommentsController.cs
  4. 2
      src/Squidex/Areas/Api/Controllers/Comments/Models/CommentDto.cs
  5. 1
      src/Squidex/app/features/content/declarations.ts
  6. 6
      src/Squidex/app/features/content/module.ts
  7. 1
      src/Squidex/app/features/content/pages/comments/comments-page.component.html
  8. 2
      src/Squidex/app/features/content/pages/comments/comments-page.component.scss
  9. 30
      src/Squidex/app/features/content/pages/comments/comments-page.component.ts
  10. 2
      src/Squidex/app/features/content/pages/content/content-history.component.html
  11. 4
      src/Squidex/app/features/content/pages/content/content-page.component.html
  12. 19
      src/Squidex/app/shared/components/comment.component.html
  13. 43
      src/Squidex/app/shared/components/comment.component.scss
  14. 38
      src/Squidex/app/shared/components/comment.component.ts
  15. 26
      src/Squidex/app/shared/components/comments.component.html
  16. 11
      src/Squidex/app/shared/components/comments.component.scss
  17. 84
      src/Squidex/app/shared/components/comments.component.ts
  18. 2
      src/Squidex/app/shared/components/history.component.html
  19. 2
      src/Squidex/app/shared/declarations.ts
  20. 1
      src/Squidex/app/shared/internal.ts
  21. 6
      src/Squidex/app/shared/module.ts
  22. 18
      src/Squidex/app/shared/services/comments.service.spec.ts
  23. 40
      src/Squidex/app/shared/services/comments.service.ts
  24. 22
      src/Squidex/app/shared/state/comments.form.ts
  25. 119
      src/Squidex/app/shared/state/comments.state.spec.ts
  26. 31
      src/Squidex/app/shared/state/comments.state.ts
  27. 8
      src/Squidex/app/theme/icomoon/demo-files/demo.css
  28. 324
      src/Squidex/app/theme/icomoon/demo.html
  29. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.eot
  30. 1
      src/Squidex/app/theme/icomoon/fonts/icomoon.svg
  31. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.ttf
  32. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.woff
  33. 2
      src/Squidex/app/theme/icomoon/selection.json
  34. 40
      src/Squidex/app/theme/icomoon/style.css

1
src/Squidex.Domain.Apps.Entities/Comments/CommentsGrain.cs

@ -65,7 +65,6 @@ namespace Squidex.Domain.Apps.Entities.Comments
if (events.Length > 0) if (events.Length > 0)
{ {
await persistence.WriteEventsAsync(events); await persistence.WriteEventsAsync(events);
await persistence.WriteSnapshotAsync(Snapshot);
} }
} }

2
src/Squidex.Domain.Apps.Events/Comments/CommentDeleted.cs

@ -10,7 +10,7 @@ using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events.Comments namespace Squidex.Domain.Apps.Events.Comments
{ {
[EventType(nameof(CommentCreated))] [EventType(nameof(CommentDeleted))]
public sealed class CommentDeleted : CommentsEvent public sealed class CommentDeleted : CommentsEvent
{ {
public Guid CommentId { get; set; } public Guid CommentId { get; set; }

32
src/Squidex/Areas/Api/Controllers/Comments/CommentsController.cs

@ -21,8 +21,10 @@ namespace Squidex.Areas.Api.Controllers.Comments
/// <summary> /// <summary>
/// Manages comments for any kind of resource. /// Manages comments for any kind of resource.
/// </summary> /// </summary>
[ApiAuthorize]
[ApiExceptionFilter] [ApiExceptionFilter]
[ApiExplorerSettings(GroupName = nameof(Languages))] [AppApi]
[ApiExplorerSettings(GroupName = nameof(Comments))]
public sealed class CommentsController : ApiController public sealed class CommentsController : ApiController
{ {
private readonly IGrainFactory grainFactory; private readonly IGrainFactory grainFactory;
@ -36,17 +38,19 @@ namespace Squidex.Areas.Api.Controllers.Comments
/// <summary> /// <summary>
/// Get all comments. /// Get all comments.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="commentsId">The id of the comments.</param> /// <param name="commentsId">The id of the comments.</param>
/// <returns> /// <returns>
/// 200 => All comments returned. /// 200 => All comments returned.
/// 404 => App not found.
/// </returns> /// </returns>
[HttpGet] [HttpGet]
[Route("comments/{commentsId}")] [Route("apps/{app}/comments/{commentsId}")]
[ProducesResponseType(typeof(CommentsDto), 200)] [ProducesResponseType(typeof(CommentsDto), 200)]
[ApiCosts(0)] [ApiCosts(0)]
public async Task<IActionResult> GetComments(Guid commentsId) public async Task<IActionResult> GetComments(string app, Guid commentsId)
{ {
if (!long.TryParse(Request.Headers["X-Since"], out var version)) if (!long.TryParse(Request.Headers["If-None-Match"], out var version))
{ {
version = EtagVersion.Any; version = EtagVersion.Any;
} }
@ -62,18 +66,20 @@ namespace Squidex.Areas.Api.Controllers.Comments
/// <summary> /// <summary>
/// Create a new comment. /// Create a new comment.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="commentsId">The id of the comments.</param> /// <param name="commentsId">The id of the comments.</param>
/// <param name="request">The comment object that needs to created.</param> /// <param name="request">The comment object that needs to created.</param>
/// <returns> /// <returns>
/// 201 => Comment created. /// 201 => Comment created.
/// 400 => Comment is not valid. /// 400 => Comment is not valid.
/// 404 => App not found.
/// </returns> /// </returns>
[HttpPost] [HttpPost]
[Route("comments/{commentdsId}")] [Route("apps/{app}/comments/{commentsId}")]
[ProducesResponseType(typeof(EntityCreatedDto), 201)] [ProducesResponseType(typeof(EntityCreatedDto), 201)]
[ProducesResponseType(typeof(ErrorDto), 400)] [ProducesResponseType(typeof(ErrorDto), 400)]
[ApiCosts(0)] [ApiCosts(0)]
public async Task<IActionResult> PostComment(Guid commentsId, [FromBody] UpsertCommentDto request) public async Task<IActionResult> PostComment(string app, Guid commentsId, [FromBody] UpsertCommentDto request)
{ {
var command = request.ToCreateCommand(commentsId); var command = request.ToCreateCommand(commentsId);
var context = await CommandBus.PublishAsync(command); var context = await CommandBus.PublishAsync(command);
@ -86,20 +92,21 @@ namespace Squidex.Areas.Api.Controllers.Comments
/// <summary> /// <summary>
/// Updates the comment. /// Updates the comment.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="commentsId">The id of the comments.</param> /// <param name="commentsId">The id of the comments.</param>
/// <param name="commentId">The id of the comment.</param> /// <param name="commentId">The id of the comment.</param>
/// <param name="request">The comment object that needs to updated.</param> /// <param name="request">The comment object that needs to updated.</param>
/// <returns> /// <returns>
/// 204 => Comment updated. /// 204 => Comment updated.
/// 400 => Comment text not valid. /// 400 => Comment text not valid.
/// 404 => Comment not found. /// 404 => Comment or app not found.
/// </returns> /// </returns>
[MustBeAppReader] [MustBeAppReader]
[HttpPut] [HttpPut]
[Route("comments/{commentdsId}/{commentId}")] [Route("apps/{app}/comments/{commentsId}/{commentId}")]
[ProducesResponseType(typeof(ErrorDto), 400)] [ProducesResponseType(typeof(ErrorDto), 400)]
[ApiCosts(0)] [ApiCosts(0)]
public async Task<IActionResult> PutComment(Guid commentsId, Guid commentId, [FromBody] UpsertCommentDto request) public async Task<IActionResult> PutComment(string app, Guid commentsId, Guid commentId, [FromBody] UpsertCommentDto request)
{ {
await CommandBus.PublishAsync(request.ToUpdateComment(commentsId, commentId)); await CommandBus.PublishAsync(request.ToUpdateComment(commentsId, commentId));
@ -109,17 +116,18 @@ namespace Squidex.Areas.Api.Controllers.Comments
/// <summary> /// <summary>
/// Deletes the comment. /// Deletes the comment.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="commentsId">The id of the comments.</param> /// <param name="commentsId">The id of the comments.</param>
/// <param name="commentId">The id of the comment.</param> /// <param name="commentId">The id of the comment.</param>
/// <returns> /// <returns>
/// 204 => Comment deleted. /// 204 => Comment deleted.
/// 404 => Comment not found. /// 404 => Comment or app not found.
/// </returns> /// </returns>
[HttpDelete] [HttpDelete]
[Route("comments/{commentdsId}/{commentId}")] [Route("apps/{app}/comments/{commentsId}/{commentId}")]
[ProducesResponseType(typeof(ErrorDto), 400)] [ProducesResponseType(typeof(ErrorDto), 400)]
[ApiCosts(0)] [ApiCosts(0)]
public async Task<IActionResult> DeleteComment(Guid commentsId, Guid commentId) public async Task<IActionResult> DeleteComment(string app, Guid commentsId, Guid commentId)
{ {
await CommandBus.PublishAsync(new DeleteComment { CommentsId = commentsId, CommentId = commentId }); await CommandBus.PublishAsync(new DeleteComment { CommentsId = commentsId, CommentId = commentId });

2
src/Squidex/Areas/Api/Controllers/Comments/Models/CommentDto.cs

@ -47,7 +47,7 @@ namespace Squidex.Areas.Api.Controllers.Comments.Models
public static CommentDto FromCommand(CreateComment command) public static CommentDto FromCommand(CreateComment command)
{ {
return SimpleMapper.Map(command, new CommentDto { User = command.Actor, Time = SystemClock.Instance.GetCurrentInstant() }); return SimpleMapper.Map(command, new CommentDto { Id = command.CommentId, User = command.Actor, Time = SystemClock.Instance.GetCurrentInstant() });
} }
} }
} }

1
src/Squidex/app/features/content/declarations.ts

@ -5,6 +5,7 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
export * from './pages/comments/comments-page.component';
export * from './pages/content/content-field.component'; export * from './pages/content/content-field.component';
export * from './pages/content/content-history.component'; export * from './pages/content/content-history.component';
export * from './pages/content/content-page.component'; export * from './pages/content/content-page.component';

6
src/Squidex/app/features/content/module.ts

@ -25,6 +25,7 @@ import {
ArrayEditorComponent, ArrayEditorComponent,
ArrayItemComponent, ArrayItemComponent,
AssetsEditorComponent, AssetsEditorComponent,
CommentsPageComponent,
ContentFieldComponent, ContentFieldComponent,
ContentHistoryComponent, ContentHistoryComponent,
ContentItemComponent, ContentItemComponent,
@ -75,6 +76,10 @@ const routes: Routes = [
data: { data: {
channel: 'contents.{contentId}' channel: 'contents.{contentId}'
} }
},
{
path: 'comments',
component: CommentsPageComponent
} }
] ]
} }
@ -95,6 +100,7 @@ const routes: Routes = [
ArrayEditorComponent, ArrayEditorComponent,
ArrayItemComponent, ArrayItemComponent,
AssetsEditorComponent, AssetsEditorComponent,
CommentsPageComponent,
ContentFieldComponent, ContentFieldComponent,
ContentHistoryComponent, ContentHistoryComponent,
ContentItemComponent, ContentItemComponent,

1
src/Squidex/app/features/content/pages/comments/comments-page.component.html

@ -0,0 +1 @@
<sqx-comments [commentsId]="commentsId"></sqx-comments>

2
src/Squidex/app/features/content/pages/comments/comments-page.component.scss

@ -0,0 +1,2 @@
@import '_vars';
@import '_mixins';

30
src/Squidex/app/features/content/pages/comments/comments-page.component.ts

@ -0,0 +1,30 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { allParams } from '@app/shared';
@Component({
selector: 'sqx-comments-page',
styleUrls: ['./comments-page.component.scss'],
templateUrl: './comments-page.component.html'
})
export class CommentsPageComponent implements OnInit {
public commentsId: string;
constructor(
private readonly route: ActivatedRoute
) {
}
public ngOnInit() {
this.commentsId = allParams(this.route)['contentId'];
}
}

2
src/Squidex/app/features/content/pages/content/content-history.component.html

@ -1,4 +1,4 @@
<sqx-panel desiredWidth="16rem" isBlank="true" [isLazyLoaded]="false"> <sqx-panel desiredWidth="20rem" isBlank="true" [isLazyLoaded]="false">
<ng-container title> <ng-container title>
Activity Activity
</ng-container> </ng-container>

4
src/Squidex/app/features/content/pages/content/content-page.component.html

@ -125,6 +125,10 @@
<i class="icon-time"></i> <i class="icon-time"></i>
</a> </a>
<a class="panel-link" routerLink="comments" routerLinkActive="active" #linkHistory>
<i class="icon-comments"></i>
</a>
<sqx-onboarding-tooltip helpId="history" [for]="linkHistory" position="leftTop" after="120000"> <sqx-onboarding-tooltip helpId="history" [for]="linkHistory" position="leftTop" after="120000">
The sidebar navigation contains useful context specific links. Here you can view the history how this schema has changed over time. The sidebar navigation contains useful context specific links. Here you can view the history how this schema has changed over time.
</sqx-onboarding-tooltip> </sqx-onboarding-tooltip>

19
src/Squidex/app/shared/components/comment.component.html

@ -0,0 +1,19 @@
<div class="comment row no-gutters">
<div class="col col-auto">
<img class="user-picture" [attr.title]="comment.user | sqxUserNameRef:null" [attr.src]="comment.user | sqxUserPictureRef" />
</div>
<div class="col pl-2">
<div class="comment-message">
<div class="user-row">
<div class="user-ref">{{comment.user | sqxUserNameRef:null}}</div>
<button *ngIf="comment.user === userId" type="button" class="btn btn-sm btn-link btn-danger item-remove" (click)="deleting.emit()!">
<i class="icon-bin2"></i>
</button>
</div>
<div>{{comment.text}}</div>
<div class="comment-created text-muted">{{comment.time | sqxFromNow}}</div>
</div>
</div>
</div>

43
src/Squidex/app/shared/components/comment.component.scss

@ -0,0 +1,43 @@
@import '_vars';
@import '_mixins';
.user-ref {
font-weight: bold;
}
.item-remove {
@include absolute(-5px, -15px, auto, auto);
display: none;
}
.user-row {
& {
position: relative;
}
&:hover {
.item-remove {
display: block;
}
}
}
.comment {
& {
font-size: .9rem;
font-weight: normal;
margin-bottom: .75rem;
}
&-message {
margin-bottom: .375rem;
}
&-created {
font-size: .75rem;
}
}
.text-muted {
color: $color-history;
}

38
src/Squidex/app/shared/components/comment.component.ts

@ -0,0 +1,38 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { CommentDto, UpsertCommentForm } from '@app/shared/internal';
@Component({
selector: 'sqx-comment',
styleUrls: ['./comment.component.scss'],
templateUrl: './comment.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CommentComponent {
public editForm = new UpsertCommentForm(this.formBuilder);
@Input()
public comment: CommentDto;
@Input()
public userId: string;
@Output()
public deleting = new EventEmitter();
@Output()
public updated = new EventEmitter<string>();
constructor(
private readonly formBuilder: FormBuilder
) {
}
}

26
src/Squidex/app/shared/components/comments.component.html

@ -0,0 +1,26 @@
<sqx-panel desiredWidth="20rem" isBlank="true" [isLazyLoaded]="false" contentClass="grid">
<ng-container title>
Comments
</ng-container>
<ng-container content>
<div class="grid-content" #scrollMe [scrollTop]="scrollMe.scrollHeight">
<sqx-comment *ngFor="let comment of state.comments | async; trackBy: trackByComment"
[comment]="comment"
[userId]="userId"
(updated)="update(comment, $event)"
(deleting)="delete(comment)">
</sqx-comment>
</div>
<div class="grid-footer">
<form [formGroup]="commentForm.form" (submit)="comment()">
<input class="form-control" name="text" formControlName="text" placeholder="Create a comment" />
</form>
</div>
</ng-container>
</sqx-panel>

11
src/Squidex/app/shared/components/comments.component.scss

@ -0,0 +1,11 @@
@import '_vars';
@import '_mixins';
.grid-footer {
border-top-width: 1px;
}
.grid-body,
.grid-footer {
padding: 1rem;
}

84
src/Squidex/app/shared/components/comments.component.ts

@ -0,0 +1,84 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Subscription, timer } from 'rxjs';
import { onErrorResumeNext, switchMap } from 'rxjs/operators';
import {
AppsState,
AuthService,
CommentDto,
CommentsService,
CommentsState,
DialogService,
fadeAnimation,
UpsertCommentForm
} from '@app/shared/internal';
@Component({
selector: 'sqx-comments',
styleUrls: ['./comments.component.scss'],
templateUrl: './comments.component.html',
animations: [
fadeAnimation
]
})
export class CommentsComponent implements OnDestroy, OnInit {
private timer: Subscription;
public state: CommentsState;
public userId: string;
public commentForm = new UpsertCommentForm(this.formBuilder);
@Input()
public commentsId: string;
constructor(authService: AuthService,
private readonly appsState: AppsState,
private readonly commentsService: CommentsService,
private readonly dialogs: DialogService,
private readonly formBuilder: FormBuilder
) {
this.userId = authService.user!.token;
}
public ngOnDestroy() {
this.timer.unsubscribe();
}
public ngOnInit() {
this.state = new CommentsState(this.appsState, this.commentsId, this.commentsService, this.dialogs);
this.timer = timer(0, 4000).pipe(switchMap(() => this.state.load()), onErrorResumeNext()).subscribe();
}
public delete(comment: CommentDto) {
this.state.delete(comment.id).pipe(onErrorResumeNext()).subscribe();
}
public update(comment: CommentDto, text: string) {
this.state.update(comment.id, text).pipe(onErrorResumeNext()).subscribe();
}
public comment() {
const value = this.commentForm.submit();
if (value) {
this.state.create(value.text).pipe(onErrorResumeNext()).subscribe();
this.commentForm.submitCompleted({});
}
}
public trackByComment(index: number, comment: CommentDto) {
return comment.id;
}
}

2
src/Squidex/app/shared/components/history.component.html

@ -1,4 +1,4 @@
<sqx-panel desiredWidth="16rem" isBlank="true" [isLazyLoaded]="false"> <sqx-panel desiredWidth="20rem" isBlank="true" [isLazyLoaded]="false">
<ng-container title> <ng-container title>
Activity Activity
</ng-container> </ng-container>

2
src/Squidex/app/shared/declarations.ts

@ -9,6 +9,8 @@ export * from './components/app-form.component';
export * from './components/asset.component'; export * from './components/asset.component';
export * from './components/assets-list.component'; export * from './components/assets-list.component';
export * from './components/assets-selector.component'; export * from './components/assets-selector.component';
export * from './components/comment.component';
export * from './components/comments.component';
export * from './components/help.component'; export * from './components/help.component';
export * from './components/geolocation-editor.component'; export * from './components/geolocation-editor.component';
export * from './components/history.component'; export * from './components/history.component';

1
src/Squidex/app/shared/internal.ts

@ -50,6 +50,7 @@ export * from './state/backups.forms';
export * from './state/backups.state'; export * from './state/backups.state';
export * from './state/clients.forms'; export * from './state/clients.forms';
export * from './state/clients.state'; export * from './state/clients.state';
export * from './state/comments.form';
export * from './state/comments.state'; export * from './state/comments.state';
export * from './state/contents.forms'; export * from './state/contents.forms';
export * from './state/contents.state'; export * from './state/contents.state';

6
src/Squidex/app/shared/module.ts

@ -34,6 +34,8 @@ import {
BackupsService, BackupsService,
BackupsState, BackupsState,
ClientsState, ClientsState,
CommentComponent,
CommentsComponent,
CommentsService, CommentsService,
CommentsState, CommentsState,
ContentMustExistGuard, ContentMustExistGuard,
@ -99,6 +101,8 @@ import {
AssetUrlPipe, AssetUrlPipe,
AssetsListComponent, AssetsListComponent,
AssetsSelectorComponent, AssetsSelectorComponent,
CommentComponent,
CommentsComponent,
FileIconPipe, FileIconPipe,
GeolocationEditorComponent, GeolocationEditorComponent,
HelpComponent, HelpComponent,
@ -124,6 +128,8 @@ import {
AssetUrlPipe, AssetUrlPipe,
AssetsListComponent, AssetsListComponent,
AssetsSelectorComponent, AssetsSelectorComponent,
CommentComponent,
CommentsComponent,
FileIconPipe, FileIconPipe,
GeolocationEditorComponent, GeolocationEditorComponent,
HelpComponent, HelpComponent,

18
src/Squidex/app/shared/services/comments.service.spec.ts

@ -42,14 +42,14 @@ describe('CommentsService', () => {
let comments: CommentsDto; let comments: CommentsDto;
commentsService.getComments('my-comments', new Version('123')).subscribe(result => { commentsService.getComments('my-app', 'my-comments', new Version('123')).subscribe(result => {
comments = result; comments = result;
}); });
const req = httpMock.expectOne('http://service/p/api/comments/my-comments'); const req = httpMock.expectOne('http://service/p/api/apps/my-app/comments/my-comments');
expect(req.request.method).toEqual('GET'); expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('X-Since')).toBe('123'); expect(req.request.headers.get('If-None-Match')).toBe('123');
req.flush({ req.flush({
createdComments: [{ createdComments: [{
@ -86,11 +86,11 @@ describe('CommentsService', () => {
let comment: CommentDto; let comment: CommentDto;
commentsService.postComment('my-comments', new UpsertCommentDto('text1')).subscribe(result => { commentsService.postComment('my-app', 'my-comments', new UpsertCommentDto('text1')).subscribe(result => {
comment = <CommentDto>result; comment = <CommentDto>result;
}); });
const req = httpMock.expectOne('http://service/p/api/comments/my-comments'); const req = httpMock.expectOne('http://service/p/api/apps/my-app/comments/my-comments');
expect(req.request.method).toEqual('POST'); expect(req.request.method).toEqual('POST');
expect(req.request.headers.get('If-Match')).toBeNull(); expect(req.request.headers.get('If-Match')).toBeNull();
@ -108,9 +108,9 @@ describe('CommentsService', () => {
it('should make put request to replace comment content', it('should make put request to replace comment content',
inject([CommentsService, HttpTestingController], (commentsService: CommentsService, httpMock: HttpTestingController) => { inject([CommentsService, HttpTestingController], (commentsService: CommentsService, httpMock: HttpTestingController) => {
commentsService.putComment('my-comments', '123', new UpsertCommentDto('text1')).subscribe(); commentsService.putComment('my-app', 'my-comments', '123', new UpsertCommentDto('text1')).subscribe();
const req = httpMock.expectOne('http://service/p/api/comments/my-comments/123'); const req = httpMock.expectOne('http://service/p/api/apps/my-app/comments/my-comments/123');
expect(req.request.method).toEqual('PUT'); expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBeNull(); expect(req.request.headers.get('If-Match')).toBeNull();
@ -121,9 +121,9 @@ describe('CommentsService', () => {
it('should make delete request to delete comment', it('should make delete request to delete comment',
inject([CommentsService, HttpTestingController], (commentsService: CommentsService, httpMock: HttpTestingController) => { inject([CommentsService, HttpTestingController], (commentsService: CommentsService, httpMock: HttpTestingController) => {
commentsService.deleteComment('my-comments', '123').subscribe(); commentsService.deleteComment('my-app', 'my-comments', '123').subscribe();
const req = httpMock.expectOne('http://service/p/api/comments/my-comments/123'); const req = httpMock.expectOne('http://service/p/api/apps/my-app/comments/my-comments/123');
expect(req.request.method).toEqual('DELETE'); expect(req.request.method).toEqual('DELETE');
expect(req.request.headers.get('If-Match')).toBeNull(); expect(req.request.headers.get('If-Match')).toBeNull();

40
src/Squidex/app/shared/services/comments.service.ts

@ -5,16 +5,17 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { HttpClient } from '@angular/common/http'; import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable, of, throwError } from 'rxjs';
import { map } from 'rxjs/operators'; import { catchError, map } from 'rxjs/operators';
import { import {
ApiUrlConfig, ApiUrlConfig,
DateTime, DateTime,
Model, Model,
pretifyError, pretifyError,
Types,
Version Version
} from '@app/framework'; } from '@app/framework';
@ -59,11 +60,26 @@ export class CommentsService {
) { ) {
} }
public getComments(commentsId: string, version: Version): Observable<CommentsDto> { public getComments(appName: string, commentsId: string, version: Version): Observable<CommentsDto> {
const url = this.apiUrl.buildUrl(`api/comments/${commentsId}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/comments/${commentsId}`);
return this.http.get(url, { headers: { 'X-Since': version.value } }).pipe( const options = {
headers: new HttpHeaders().set('If-None-Match', version.value)
};
return this.http.get(url, options).pipe(
catchError(err => {
if (err.status === 304) {
return of(new CommentsDto([], [], [], version));
}
return throwError(err);
}),
map(response => { map(response => {
if (Types.is(response, CommentsDto)) {
return response;
}
const body: any = response; const body: any = response;
return new CommentsDto( return new CommentsDto(
@ -88,8 +104,8 @@ export class CommentsService {
pretifyError('Failed to load comments.')); pretifyError('Failed to load comments.'));
} }
public postComment(commentsId: string, dto: UpsertCommentDto): Observable<CommentDto> { public postComment(appName: string, commentsId: string, dto: UpsertCommentDto): Observable<CommentDto> {
const url = this.apiUrl.buildUrl(`api/comments/${commentsId}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/comments/${commentsId}`);
return this.http.post(url, dto).pipe( return this.http.post(url, dto).pipe(
map(response => { map(response => {
@ -104,8 +120,8 @@ export class CommentsService {
pretifyError('Failed to create comment.')); pretifyError('Failed to create comment.'));
} }
public putComment(commentsId: string, commentId: string, dto: UpsertCommentDto): Observable<any> { public putComment(appName: string, commentsId: string, commentId: string, dto: UpsertCommentDto): Observable<any> {
const url = this.apiUrl.buildUrl(`api/comments/${commentsId}/${commentId}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/comments/${commentsId}/${commentId}`);
return this.http.put(url, dto).pipe( return this.http.put(url, dto).pipe(
map(response => { map(response => {
@ -120,8 +136,8 @@ export class CommentsService {
pretifyError('Failed to update comment.')); pretifyError('Failed to update comment.'));
} }
public deleteComment(commentsId: string, commentId: string): Observable<any> { public deleteComment(appName: string, commentsId: string, commentId: string): Observable<any> {
const url = this.apiUrl.buildUrl(`api/comments/${commentsId}/${commentId}`); const url = this.apiUrl.buildUrl(`api/apps/${appName}/comments/${commentsId}/${commentId}`);
return this.http.delete(url).pipe( return this.http.delete(url).pipe(
pretifyError('Failed to delete comment.')); pretifyError('Failed to delete comment.'));

22
src/Squidex/app/shared/state/comments.form.ts

@ -0,0 +1,22 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Form } from '@app/framework';
export class UpsertCommentForm extends Form<FormGroup> {
constructor(formBuilder: FormBuilder) {
super(formBuilder.group({
text: ['',
[
Validators.required
]
]
}));
}
}

119
src/Squidex/app/shared/state/comments.state.spec.ts

@ -0,0 +1,119 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { of } from 'rxjs';
import { IMock, It, Mock, Times } from 'typemoq';
import {
AppsState,
CommentDto,
CommentsDto,
CommentsService,
CommentsState,
DateTime,
DialogService,
ImmutableArray,
UpsertCommentDto,
Version
} from '@app/shared';
describe('CommentsState', () => {
const app = 'my-app';
const commentsId = 'my-comments';
const now = DateTime.today();
const user = 'not-me';
const oldComments = new CommentsDto([
new CommentDto('1', now, 'text1', user),
new CommentDto('2', now, 'text2', user)
], [], [], new Version('1'));
let dialogs: IMock<DialogService>;
let appsState: IMock<AppsState>;
let commentsService: IMock<CommentsService>;
let commentsState: CommentsState;
beforeEach(() => {
dialogs = Mock.ofType<DialogService>();
appsState = Mock.ofType<AppsState>();
appsState.setup(x => x.appName)
.returns(() => app);
commentsService = Mock.ofType<CommentsService>();
commentsService.setup(x => x.getComments(app, commentsId, new Version('')))
.returns(() => of(oldComments));
commentsState = new CommentsState(appsState.object, commentsId, commentsService.object, dialogs.object);
commentsState.load().subscribe();
});
it('should load and merge comments', () => {
const newComments = new CommentsDto([
new CommentDto('3', now, 'text3', user)
], [
new CommentDto('2', now, 'text2_2', user)
], ['1'], new Version('2'));
commentsService.setup(x => x.getComments(app, commentsId, new Version('1')))
.returns(() => of(newComments));
commentsState.load().subscribe();
expect(commentsState.snapshot.isLoaded).toBeTruthy();
expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([
new CommentDto('2', now, 'text2_2', user),
new CommentDto('3', now, 'text3', user)
]));
commentsService.verify(x => x.getComments(app, commentsId, It.isAny()), Times.exactly(2));
});
it('should add comment to snapshot when created', () => {
const newComment = new CommentDto('3', now, 'text3', user);
commentsService.setup(x => x.postComment(app, commentsId, new UpsertCommentDto('text3')))
.returns(() => of(newComment));
commentsState.create('text3').subscribe();
expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([
new CommentDto('1', now, 'text1', user),
new CommentDto('2', now, 'text2', user),
new CommentDto('3', now, 'text3', user)
]));
});
it('should update properties when updated', () => {
commentsService.setup(x => x.putComment(app, commentsId, '2', new UpsertCommentDto('text2_2')))
.returns(() => of({}));
commentsState.update('2', 'text2_2', now).subscribe();
expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([
new CommentDto('1', now, 'text1', user),
new CommentDto('2', now, 'text2_2', user)
]));
commentsService.verify(x => x.putComment(app, commentsId, '2', new UpsertCommentDto('text2_2')), Times.once());
});
it('should remove comment from snapshot when deleted', () => {
commentsService.setup(x => x.deleteComment(app, commentsId, '2'))
.returns(() => of({}));
commentsState.delete('2').subscribe();
expect(commentsState.snapshot.comments).toEqual(ImmutableArray.of([
new CommentDto('1', now, 'text1', user)
]));
commentsService.verify(x => x.deleteComment(app, commentsId, '2'), Times.once());
});
});

31
src/Squidex/app/shared/state/comments.state.ts

@ -24,6 +24,8 @@ import {
UpsertCommentDto UpsertCommentDto
} from './../services/comments.service'; } from './../services/comments.service';
import { AppsState } from './apps.state';
interface Snapshot { interface Snapshot {
comments: ImmutableArray<CommentDto>; comments: ImmutableArray<CommentDto>;
@ -43,6 +45,7 @@ export class CommentsState extends State<Snapshot> {
distinctUntilChanged()); distinctUntilChanged());
constructor( constructor(
private readonly appsState: AppsState,
private readonly commentsId: string, private readonly commentsId: string,
private readonly commentsService: CommentsService, private readonly commentsService: CommentsService,
private readonly dialogs: DialogService private readonly dialogs: DialogService
@ -51,14 +54,16 @@ export class CommentsState extends State<Snapshot> {
} }
public load(): Observable<any> { public load(): Observable<any> {
return this.commentsService.getComments(this.commentsId, this.version).pipe( return this.commentsService.getComments(this.appName, this.commentsId, this.version).pipe(
tap(dtos => { tap(dtos => {
this.next(s => { this.next(s => {
let comments = s.comments; let comments = s.comments;
for (let created of dtos.createdComments) { for (let created of dtos.createdComments) {
if (!comments.find(x => x.id === created.id)) {
comments = comments.push(created); comments = comments.push(created);
} }
}
for (let updated of dtos.updatedComments) { for (let updated of dtos.updatedComments) {
comments = comments.replaceBy('id', updated); comments = comments.replaceBy('id', updated);
@ -74,8 +79,8 @@ export class CommentsState extends State<Snapshot> {
notify(this.dialogs)); notify(this.dialogs));
} }
public create(request: UpsertCommentDto): Observable<any> { public create(text: string): Observable<any> {
return this.commentsService.postComment(this.commentsId, request).pipe( return this.commentsService.postComment(this.appName, this.commentsId, new UpsertCommentDto(text)).pipe(
tap(dto => { tap(dto => {
this.next(s => { this.next(s => {
const comments = s.comments.push(dto); const comments = s.comments.push(dto);
@ -86,11 +91,11 @@ export class CommentsState extends State<Snapshot> {
notify(this.dialogs)); notify(this.dialogs));
} }
public update(commentId: string, request: UpsertCommentDto, now?: DateTime): Observable<any> { public update(commentId: string, text: string, now?: DateTime): Observable<any> {
return this.commentsService.putComment(this.commentsId, commentId, request).pipe( return this.commentsService.putComment(this.appName, this.commentsId, commentId, new UpsertCommentDto(text)).pipe(
tap(() => { tap(() => {
this.next(s => { this.next(s => {
const comments = s.comments.map(c => c.id === commentId ? update(c, request, now || DateTime.now()) : c); const comments = s.comments.map(c => c.id === commentId ? update(c, text, now || DateTime.now()) : c);
return { ...s, comments }; return { ...s, comments };
}); });
@ -99,12 +104,12 @@ export class CommentsState extends State<Snapshot> {
} }
public delete(commentId: string): Observable<any> { public delete(commentId: string): Observable<any> {
return this.commentsService.deleteComment(this.commentsId, commentId).pipe( return this.commentsService.deleteComment(this.appName, this.commentsId, commentId).pipe(
tap(dto => { tap(() => {
this.next(s => { this.next(s => {
const comments = s.comments.filter(c => c.id !== commentId); const comments = s.comments.filter(c => c.id !== commentId);
return { ...s, comments, version: dto.version }; return { ...s, comments };
}); });
}), }),
notify(this.dialogs)); notify(this.dialogs));
@ -113,7 +118,11 @@ export class CommentsState extends State<Snapshot> {
private get version() { private get version() {
return this.snapshot.version; return this.snapshot.version;
} }
private get appName() {
return this.appsState.appName;
}
} }
const update = (comment: CommentDto, request: UpsertCommentDto, now: DateTime) => const update = (comment: CommentDto, text: string, time: DateTime) =>
comment.with({ text: request.text, time: now }); comment.with({ text, time });

8
src/Squidex/app/theme/icomoon/demo-files/demo.css

@ -147,19 +147,19 @@ p {
font-size: 16px; font-size: 16px;
} }
.fs1 { .fs1 {
font-size: 32px; font-size: 24px;
} }
.fs2 { .fs2 {
font-size: 20px; font-size: 32px;
} }
.fs3 { .fs3 {
font-size: 32px; font-size: 20px;
} }
.fs4 { .fs4 {
font-size: 32px; font-size: 32px;
} }
.fs5 { .fs5 {
font-size: 24px; font-size: 32px;
} }
.fs6 { .fs6 {
font-size: 28px; font-size: 28px;

324
src/Squidex/app/theme/icomoon/demo.html

@ -9,11 +9,94 @@
<link rel="stylesheet" href="style.css"></head> <link rel="stylesheet" href="style.css"></head>
<body> <body>
<div class="bgc1 clearfix"> <div class="bgc1 clearfix">
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs:&nbsp;98)</small></h1> <h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs:&nbsp;99)</small></h1>
</div> </div>
<div class="clearfix mhl ptl"> <div class="clearfix mhl ptl">
<h1 class="mvm mtn fgc1">Grid Size: 16</h1> <h1 class="mvm mtn fgc1">Grid Size: 24</h1>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-comments">
</span>
<span class="mls"> icon-comments</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e95f" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe95f;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-backup">
</span>
<span class="mls"> icon-backup</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e95b" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe95b;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-support">
</span>
<span class="mls"> icon-support</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e95a" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe95a;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-control-RichText">
</span>
<span class="mls"> icon-control-RichText</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e939" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe939;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1"> <div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-download">
</span>
<span class="mls"> icon-download</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e93e" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe93e;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
</div>
<div class="clearfix mhl ptl">
<h1 class="mvm mtn fgc1">Grid Size: 16</h1>
<div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-spinner2"> <span class="icon-spinner2">
@ -29,7 +112,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-star-full"> <span class="icon-star-full">
@ -45,7 +128,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-star-empty"> <span class="icon-star-empty">
@ -61,7 +144,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-twitter"> <span class="icon-twitter">
@ -77,7 +160,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-hour-glass"> <span class="icon-hour-glass">
@ -93,7 +176,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-spinner"> <span class="icon-spinner">
@ -109,7 +192,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-clock"> <span class="icon-clock">
@ -125,7 +208,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-bin2"> <span class="icon-bin2">
@ -141,7 +224,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-earth"> <span class="icon-earth">
@ -157,7 +240,7 @@
<input type="text" readonly value="earth, globe2" class="liga unitRight" /> <input type="text" readonly value="earth, globe2" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-elapsed"> <span class="icon-elapsed">
@ -173,7 +256,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-google"> <span class="icon-google">
@ -189,7 +272,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-lock"> <span class="icon-lock">
@ -205,7 +288,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-microsoft"> <span class="icon-microsoft">
@ -221,7 +304,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-pause"> <span class="icon-pause">
@ -237,7 +320,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-play"> <span class="icon-play">
@ -253,7 +336,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-reset"> <span class="icon-reset">
@ -269,7 +352,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-settings2"> <span class="icon-settings2">
@ -285,7 +368,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-timeout"> <span class="icon-timeout">
@ -301,7 +384,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs1"> <div class="glyph fs2">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-unlocked"> <span class="icon-unlocked">
@ -320,7 +403,7 @@
</div> </div>
<div class="clearfix mhl ptl"> <div class="clearfix mhl ptl">
<h1 class="mvm mtn fgc1">Grid Size: 20</h1> <h1 class="mvm mtn fgc1">Grid Size: 20</h1>
<div class="glyph fs2"> <div class="glyph fs3">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-grid1"> <span class="icon-grid1">
@ -336,7 +419,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs2"> <div class="glyph fs3">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-list"> <span class="icon-list">
@ -352,7 +435,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs2"> <div class="glyph fs3">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-info"> <span class="icon-info">
@ -368,26 +451,10 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs2">
<div class="clearfix bshadow0 pbs">
<span class="icon-control-Stars">
</span>
<span class="mls"> icon-control-Stars</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e93a" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe93a;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
</div> </div>
<div class="clearfix mhl ptl"> <div class="clearfix mhl ptl">
<h1 class="mvm mtn fgc1">Grid Size: 32</h1> <h1 class="mvm mtn fgc1">Grid Size: 32</h1>
<div class="glyph fs3"> <div class="glyph fs4">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-control-Color"> <span class="icon-control-Color">
@ -403,7 +470,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs3"> <div class="glyph fs4">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-browser"> <span class="icon-browser">
@ -419,7 +486,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs3"> <div class="glyph fs4">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-checkmark"> <span class="icon-checkmark">
@ -435,7 +502,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs3"> <div class="glyph fs4">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-control-Stars"> <span class="icon-control-Stars">
@ -454,7 +521,7 @@
</div> </div>
<div class="clearfix mhl ptl"> <div class="clearfix mhl ptl">
<h1 class="mvm mtn fgc1">Grid Size: Unknown</h1> <h1 class="mvm mtn fgc1">Grid Size: Unknown</h1>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-prerender"> <span class="icon-prerender">
@ -470,7 +537,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-circle"> <span class="icon-circle">
@ -486,7 +553,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-control-Slug"> <span class="icon-control-Slug">
@ -502,7 +569,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-type-Tags"> <span class="icon-type-Tags">
@ -518,7 +585,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-activity"> <span class="icon-activity">
@ -534,7 +601,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-history"> <span class="icon-history">
@ -550,7 +617,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-time"> <span class="icon-time">
@ -566,7 +633,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-add"> <span class="icon-add">
@ -582,7 +649,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-plus"> <span class="icon-plus">
@ -598,7 +665,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-check-circle"> <span class="icon-check-circle">
@ -614,7 +681,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-check-circle-filled"> <span class="icon-check-circle-filled">
@ -630,7 +697,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-close"> <span class="icon-close">
@ -646,7 +713,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-type-References"> <span class="icon-type-References">
@ -662,7 +729,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-control-Checkbox"> <span class="icon-control-Checkbox">
@ -678,7 +745,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-control-Dropdown"> <span class="icon-control-Dropdown">
@ -694,7 +761,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-control-Input"> <span class="icon-control-Input">
@ -710,7 +777,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-control-Radio"> <span class="icon-control-Radio">
@ -726,7 +793,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-control-TextArea"> <span class="icon-control-TextArea">
@ -742,7 +809,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-control-Toggle"> <span class="icon-control-Toggle">
@ -758,7 +825,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-copy"> <span class="icon-copy">
@ -774,7 +841,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-dashboard"> <span class="icon-dashboard">
@ -790,7 +857,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-delete"> <span class="icon-delete">
@ -806,7 +873,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-bin"> <span class="icon-bin">
@ -822,7 +889,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-delete-filled"> <span class="icon-delete-filled">
@ -838,7 +905,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-document-delete"> <span class="icon-document-delete">
@ -854,7 +921,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-document-disable"> <span class="icon-document-disable">
@ -870,7 +937,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-document-publish"> <span class="icon-document-publish">
@ -886,7 +953,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-drag"> <span class="icon-drag">
@ -902,7 +969,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-filter"> <span class="icon-filter">
@ -918,7 +985,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-github"> <span class="icon-github">
@ -934,7 +1001,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-help"> <span class="icon-help">
@ -950,7 +1017,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-location"> <span class="icon-location">
@ -966,7 +1033,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-control-Map"> <span class="icon-control-Map">
@ -982,7 +1049,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-type-Geolocation"> <span class="icon-type-Geolocation">
@ -998,7 +1065,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-logo"> <span class="icon-logo">
@ -1014,7 +1081,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-media"> <span class="icon-media">
@ -1030,7 +1097,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-type-Assets"> <span class="icon-type-Assets">
@ -1046,7 +1113,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-trigger-AssetChanged"> <span class="icon-trigger-AssetChanged">
@ -1062,7 +1129,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-more"> <span class="icon-more">
@ -1078,7 +1145,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-dots"> <span class="icon-dots">
@ -1094,7 +1161,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-pencil"> <span class="icon-pencil">
@ -1110,7 +1177,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-reference"> <span class="icon-reference">
@ -1126,7 +1193,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-schemas"> <span class="icon-schemas">
@ -1142,7 +1209,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-search"> <span class="icon-search">
@ -1158,7 +1225,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-settings"> <span class="icon-settings">
@ -1174,7 +1241,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-type-Boolean"> <span class="icon-type-Boolean">
@ -1190,7 +1257,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-type-DateTime"> <span class="icon-type-DateTime">
@ -1206,7 +1273,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-type-Json"> <span class="icon-type-Json">
@ -1222,7 +1289,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-json"> <span class="icon-json">
@ -1238,7 +1305,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-type-Number"> <span class="icon-type-Number">
@ -1254,7 +1321,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-type-String"> <span class="icon-type-String">
@ -1270,7 +1337,7 @@
<input type="text" readonly value="" class="liga unitRight" /> <input type="text" readonly value="" class="liga unitRight" />
</div> </div>
</div> </div>
<div class="glyph fs4"> <div class="glyph fs5">
<div class="clearfix bshadow0 pbs"> <div class="clearfix bshadow0 pbs">
<span class="icon-user"> <span class="icon-user">
@ -1287,73 +1354,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="clearfix mhl ptl">
<h1 class="mvm mtn fgc1">Grid Size: 24</h1>
<div class="glyph fs5">
<div class="clearfix bshadow0 pbs">
<span class="icon-backup">
</span>
<span class="mls"> icon-backup</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e95b" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe95b;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs5">
<div class="clearfix bshadow0 pbs">
<span class="icon-support">
</span>
<span class="mls"> icon-support</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e95a" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe95a;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs5">
<div class="clearfix bshadow0 pbs">
<span class="icon-control-RichText">
</span>
<span class="mls"> icon-control-RichText</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e939" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe939;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs5">
<div class="clearfix bshadow0 pbs">
<span class="icon-download">
</span>
<span class="mls"> icon-download</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e93e" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe93e;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
</div>
<div class="clearfix mhl ptl"> <div class="clearfix mhl ptl">
<h1 class="mvm mtn fgc1">Grid Size: 14</h1> <h1 class="mvm mtn fgc1">Grid Size: 14</h1>
<div class="glyph fs6"> <div class="glyph fs6">

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.eot

Binary file not shown.

1
src/Squidex/app/theme/icomoon/fonts/icomoon.svg

@ -102,6 +102,7 @@
<glyph unicode="&#xe95c;" glyph-name="twitter" d="M1024 733.6c-37.6-16.8-78.2-28-120.6-33 43.4 26 76.6 67.2 92.4 116.2-40.6-24-85.6-41.6-133.4-51-38.4 40.8-93 66.2-153.4 66.2-116 0-210-94-210-210 0-16.4 1.8-32.4 5.4-47.8-174.6 8.8-329.4 92.4-433 219.6-18-31-28.4-67.2-28.4-105.6 0-72.8 37-137.2 93.4-174.8-34.4 1-66.8 10.6-95.2 26.2 0-0.8 0-1.8 0-2.6 0-101.8 72.4-186.8 168.6-206-17.6-4.8-36.2-7.4-55.4-7.4-13.6 0-26.6 1.4-39.6 3.8 26.8-83.4 104.4-144.2 196.2-146-72-56.4-162.4-90-261-90-17 0-33.6 1-50.2 3 93.2-59.8 203.6-94.4 322.2-94.4 386.4 0 597.8 320.2 597.8 597.8 0 9.2-0.2 18.2-0.6 27.2 41 29.4 76.6 66.4 104.8 108.6z" /> <glyph unicode="&#xe95c;" glyph-name="twitter" d="M1024 733.6c-37.6-16.8-78.2-28-120.6-33 43.4 26 76.6 67.2 92.4 116.2-40.6-24-85.6-41.6-133.4-51-38.4 40.8-93 66.2-153.4 66.2-116 0-210-94-210-210 0-16.4 1.8-32.4 5.4-47.8-174.6 8.8-329.4 92.4-433 219.6-18-31-28.4-67.2-28.4-105.6 0-72.8 37-137.2 93.4-174.8-34.4 1-66.8 10.6-95.2 26.2 0-0.8 0-1.8 0-2.6 0-101.8 72.4-186.8 168.6-206-17.6-4.8-36.2-7.4-55.4-7.4-13.6 0-26.6 1.4-39.6 3.8 26.8-83.4 104.4-144.2 196.2-146-72-56.4-162.4-90-261-90-17 0-33.6 1-50.2 3 93.2-59.8 203.6-94.4 322.2-94.4 386.4 0 597.8 320.2 597.8 597.8 0 9.2-0.2 18.2-0.6 27.2 41 29.4 76.6 66.4 104.8 108.6z" />
<glyph unicode="&#xe95d;" glyph-name="star-full" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538z" /> <glyph unicode="&#xe95d;" glyph-name="star-full" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538z" />
<glyph unicode="&#xe95e;" glyph-name="star-empty" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538zM512 206.502l-223.462-117.48 42.676 248.83-180.786 176.222 249.84 36.304 111.732 226.396 111.736-226.396 249.836-36.304-180.788-176.222 42.678-248.83-223.462 117.48z" /> <glyph unicode="&#xe95e;" glyph-name="star-empty" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538zM512 206.502l-223.462-117.48 42.676 248.83-180.786 176.222 249.84 36.304 111.732 226.396 111.736-226.396 249.836-36.304-180.788-176.222 42.678-248.83-223.462 117.48z" />
<glyph unicode="&#xe95f;" glyph-name="comments" d="M854 256.667v512h-684v-598l86 86h598zM854 852.667c46 0 84-38 84-84v-512c0-46-38-86-84-86h-598l-170-170v768c0 46 38 84 84 84h684z" />
<glyph unicode="&#xe9ca;" glyph-name="earth" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512-0.002c-62.958 0-122.872 13.012-177.23 36.452l233.148 262.29c5.206 5.858 8.082 13.422 8.082 21.26v96c0 17.674-14.326 32-32 32-112.99 0-232.204 117.462-233.374 118.626-6 6.002-14.14 9.374-22.626 9.374h-128c-17.672 0-32-14.328-32-32v-192c0-12.122 6.848-23.202 17.69-28.622l110.31-55.156v-187.886c-116.052 80.956-192 215.432-192 367.664 0 68.714 15.49 133.806 43.138 192h116.862c8.488 0 16.626 3.372 22.628 9.372l128 128c6 6.002 9.372 14.14 9.372 22.628v77.412c40.562 12.074 83.518 18.588 128 18.588 70.406 0 137.004-16.26 196.282-45.2-4.144-3.502-8.176-7.164-12.046-11.036-36.266-36.264-56.236-84.478-56.236-135.764s19.97-99.5 56.236-135.764c36.434-36.432 85.218-56.264 135.634-56.26 3.166 0 6.342 0.080 9.518 0.236 13.814-51.802 38.752-186.656-8.404-372.334-0.444-1.744-0.696-3.488-0.842-5.224-81.324-83.080-194.7-134.656-320.142-134.656z" /> <glyph unicode="&#xe9ca;" glyph-name="earth" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512-0.002c-62.958 0-122.872 13.012-177.23 36.452l233.148 262.29c5.206 5.858 8.082 13.422 8.082 21.26v96c0 17.674-14.326 32-32 32-112.99 0-232.204 117.462-233.374 118.626-6 6.002-14.14 9.374-22.626 9.374h-128c-17.672 0-32-14.328-32-32v-192c0-12.122 6.848-23.202 17.69-28.622l110.31-55.156v-187.886c-116.052 80.956-192 215.432-192 367.664 0 68.714 15.49 133.806 43.138 192h116.862c8.488 0 16.626 3.372 22.628 9.372l128 128c6 6.002 9.372 14.14 9.372 22.628v77.412c40.562 12.074 83.518 18.588 128 18.588 70.406 0 137.004-16.26 196.282-45.2-4.144-3.502-8.176-7.164-12.046-11.036-36.266-36.264-56.236-84.478-56.236-135.764s19.97-99.5 56.236-135.764c36.434-36.432 85.218-56.264 135.634-56.26 3.166 0 6.342 0.080 9.518 0.236 13.814-51.802 38.752-186.656-8.404-372.334-0.444-1.744-0.696-3.488-0.842-5.224-81.324-83.080-194.7-134.656-320.142-134.656z" />
<glyph unicode="&#xf00a;" glyph-name="grid" d="M292.571 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857z" /> <glyph unicode="&#xf00a;" glyph-name="grid" d="M292.571 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857z" />
<glyph unicode="&#xf0c9;" glyph-name="list1" horiz-adv-x="878" d="M877.714 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 475.428v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 768v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571z" /> <glyph unicode="&#xf0c9;" glyph-name="list1" horiz-adv-x="878" d="M877.714 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 475.428v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 768v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571z" />

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.ttf

Binary file not shown.

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.woff

Binary file not shown.

2
src/Squidex/app/theme/icomoon/selection.json

File diff suppressed because one or more lines are too long

40
src/Squidex/app/theme/icomoon/style.css

@ -1,10 +1,10 @@
@font-face { @font-face {
font-family: 'icomoon'; font-family: 'icomoon';
src: url('fonts/icomoon.eot?z1dhmx'); src: url('fonts/icomoon.eot?jlrdyp');
src: url('fonts/icomoon.eot?z1dhmx#iefix') format('embedded-opentype'), src: url('fonts/icomoon.eot?jlrdyp#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?z1dhmx') format('truetype'), url('fonts/icomoon.ttf?jlrdyp') format('truetype'),
url('fonts/icomoon.woff?z1dhmx') format('woff'), url('fonts/icomoon.woff?jlrdyp') format('woff'),
url('fonts/icomoon.svg?z1dhmx#icomoon') format('svg'); url('fonts/icomoon.svg?jlrdyp#icomoon') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@ -24,6 +24,21 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-comments:before {
content: "\e95f";
}
.icon-backup:before {
content: "\e95b";
}
.icon-support:before {
content: "\e95a";
}
.icon-control-RichText:before {
content: "\e939";
}
.icon-download:before {
content: "\e93e";
}
.icon-spinner2:before { .icon-spinner2:before {
content: "\e959"; content: "\e959";
} }
@ -90,9 +105,6 @@
.icon-info:before { .icon-info:before {
content: "\e93c"; content: "\e93c";
} }
.icon-control-Stars:before {
content: "\e93a";
}
.icon-control-Color:before { .icon-control-Color:before {
content: "\e94d"; content: "\e94d";
} }
@ -261,18 +273,6 @@
.icon-user:before { .icon-user:before {
content: "\e928"; content: "\e928";
} }
.icon-backup:before {
content: "\e95b";
}
.icon-support:before {
content: "\e95a";
}
.icon-control-RichText:before {
content: "\e939";
}
.icon-download:before {
content: "\e93e";
}
.icon-single-content:before { .icon-single-content:before {
content: "\e958"; content: "\e958";
} }

Loading…
Cancel
Save