Initial commit

This commit is contained in:
zhongjin
2020-06-15 10:58:47 +08:00
commit 4f1dfe7564
8590 changed files with 1516878 additions and 0 deletions
@@ -0,0 +1,2 @@
<div class="js-dialogIconPicker">
</div>
@@ -0,0 +1,68 @@
const CoreView = require('backbone/core-view');
const checkAndBuildOpts = require('builder/helpers/required-opts');
const OrganizationIconsView = require('../icons/organization-icons-view');
const template = require('./icon-picker-dialog.tpl');
const REQUIRED_OPTS = [
'orgId',
'configModel'
];
module.exports = CoreView.extend({
className: 'Dialog Modal IconPickerDialog',
events: {
'click .js-addIcon': '_onAddIconClicked'
},
initialize: function (options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
},
render: function () {
this.clearSubViews();
this.$el.html(template());
this.$('.content').addClass('Dialog-content--expanded');
this._initViews();
return this;
},
_initViews: function () {
this.icon_picker = new OrganizationIconsView({
el: this.$('.js-dialogIconPicker'),
orgId: this._orgId,
configModel: this._configModel
});
this.addView(this.icon_picker);
this.icon_picker.model.on('change:isProcessRunning', this._onIsProcessRunningChanged, this);
},
_onIsProcessRunningChanged: function () {
var running = this.icon_picker.model.get('isProcessRunning');
if (running) {
this.$el.css('pointer-events', 'none');
} else {
this.$el.css('pointer-events', 'auto');
}
},
_onAddIconClicked: function (event) {
this.killEvent(event);
this._hideErrorMessage();
this.$('.js-inputFile').trigger('click');
},
_hideErrorMessage: function () {
this._hide('.js-errorMessage');
},
_hide: function (selector) {
this.$(selector).addClass('is-hidden');
}
});
@@ -0,0 +1,18 @@
<div class="Modal-header">
<div class="Modal-headerContainer">
<h2 class="CDB-Text CDB-Size-huge is-light u-bSpace">Organization icons</h2>
<h3 class="CDB-Text CDB-Size-medium u-altTextColor"><a data-type="upload_file" href="#/upload" class="js-addIcon">Upload organization icons</a></h3>
</div>
</div>
<div class="Modal-container IconModal">
<div class="Tab-pane">
<div class="Modal-inner">
<div class="Dialog-body Dialog-body--expanded Dialog-body--create Dialog-body--withoutBorder">
<div class="u-inner">
<div class="js-dialogIconPicker"></div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,3 @@
<li class="CDB-NavMenu-item is-disabled js-your-icons<%- pane === 'your_icons' ? ' is-selected' : '' %>">
<button data-type="your_icons" class="CDB-NavMenu-link js-item u-upperCase">Organization icons</button>
</li>
@@ -0,0 +1,36 @@
const CoreView = require('backbone/core-view');
const template = require('./delete-icons-dialog.tpl');
const checkAndBuildOpts = require('builder/helpers/required-opts');
var REQUIRED_OPTS = [
'modalModel',
'onSubmit'
];
module.exports = CoreView.extend({
events: {
'click .js-submit': '_onSubmitClicked',
'click .js-cancel': '_closeDialog'
},
initialize: function (options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
this._numOfIcons = this.options.numOfIcons || 0;
},
render: function () {
this.$el.html(template({
numOfIcons: this._numOfIcons
}));
},
_onSubmitClicked: function () {
this._onSubmit();
this._closeDialog();
},
_closeDialog: function () {
this._modalModel.destroy();
}
});
@@ -0,0 +1,40 @@
<div class="CDB-Text Dialog-header u-inner">
<div class="Dialog-headerIcon Dialog-headerIcon--negative">
<i class="CDB-IconFont CDB-IconFont-trash"></i>
<% if (numOfIcons > 0) { %>
<span class="Badge Badge--negative Dialog-headerIconBadge CDB-Text CDB-Size-small"><%- numOfIcons %></span>
<% } %>
</div>
<h4 class="CDB-Text CDB-Size-large u-mainTextColor u-secondaryTextColor u-bSpace--m u-tSpace-xl">
You are deleting
<% if (numOfIcons <= 0) { %>
icons
<% } else if (numOfIcons === 1) { %>
1 icon
<% } else { %>
<%- numOfIcons %> icons
<% } %>
from your organization.
</h4>
<p class="CDB-Text CDB-Size-medium u-altTextColor">
Once icons are deleted they cannot be recovered. Any maps within the organization that use
<% if (numOfIcons === 1) { %>
this icon
<% } else { %>
these icons
<% } %>
will be adversely affected.
</p>
<p class="CDB-Text CDB-Size-medium u-altTextColor">Please be sure before proceeding.</p>
</div>
<div class="Dialog-footer Dialog-footer--simple u-inner">
<div class="Dialog-footerContent">
<button class="CDB-Button CDB-Button--secondary js-cancel">
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-small u-upperCase">Cancel</span>
</button>
<button class="u-lSpace--xl CDB-Button CDB-Button--error js-submit">
<span class="CDB-Button-Text CDB-Text is-semibold CDB-Size-small u-upperCase">Ok, delete</span>
</button>
</div>
</div>
@@ -0,0 +1,26 @@
const Backbone = require('backbone');
const IconModel = require('./organization-icon-model');
const checkAndBuildOpts = require('builder/helpers/required-opts');
const REQUIRED_OPTS = [
'orgId',
'configModel'
];
module.exports = Backbone.Collection.extend({
initialize: function (models, options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
},
model: function (attrs, opts) {
const options = { ...opts, configModel: opts.collection._configModel };
return new IconModel(attrs, options);
},
url: function (method) {
const version = this._configModel.urlVersion('organization-assets', method);
return `/api/${version}/organization/${this._orgId}/assets`;
}
});
@@ -0,0 +1,30 @@
const Backbone = require('backbone');
const _ = require('underscore');
const checkAndBuildOpts = require('builder/helpers/required-opts');
const REQUIRED_OPTS = [
'configModel'
];
module.exports = Backbone.Model.extend({
defaults: {
selected: false,
visible: false,
deleted: false
},
fileAttribute: 'resource',
initialize: function (models, options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
},
save: function (attrs, options = {}) {
attrs = attrs || _.clone(this.attributes);
attrs = _.omit(attrs, ['selected', 'visible', 'deleted']);
options.data = JSON.stringify(attrs);
return Backbone.Model.prototype.save.call(this, attrs, options);
}
});
@@ -0,0 +1,48 @@
const _ = require('underscore');
const CoreView = require('backbone/core-view');
const template = require('./organization-icon.tpl');
module.exports = CoreView.extend({
tagName: 'li',
className: 'IconList-item IconList-item--small',
events: {
'click': '_onClick'
},
initialize: function (options) {
if (_.isUndefined(options.model)) {
throw new Error('An organization icon model is mandatory.');
}
this._initBinds();
},
render: function () {
this.$el.html(template({
url: this.model.get('public_url')
}));
return this;
},
_initBinds: function () {
this.listenTo(this.model, 'change:selected', this._onSelectedChanged);
this.listenTo(this.model, 'change:deleted', this._onDeletedChanged);
},
_onClick: function () {
this.model.set('selected', !this.model.get('selected'));
},
_onSelectedChanged: function () {
this.$el.toggleClass('is-selected', this.model.get('selected'));
},
_onDeletedChanged: function () {
if (this.model.get('deleted')) {
this.$el.remove();
}
}
});
@@ -0,0 +1,3 @@
<div class="IconItem-icon js-asset">
<img height="24" src="<%- url %>" alt="icon" crossorigin="anonymous" />
</div>
@@ -0,0 +1,349 @@
const Backbone = require('backbone');
const CoreView = require('backbone/core-view');
const _ = require('underscore');
const IconCollection = require('./organization-icon-collection');
const IconView = require('./organization-icon-view');
const DeleteIconsDialog = require('./delete-icons-dialog-view');
const ModalsServiceModel = require('builder/components/modals/modals-service-model');
const template = require('./organization-icons.tpl');
const checkAndBuildOpts = require('builder/helpers/required-opts');
const REQUIRED_OPTS = [
'orgId',
'configModel'
];
module.exports = CoreView.extend({
events: {
'click .js-addIcon': '_onAddIconClicked',
'click .js-selectAllIcons': '_onSelectAllIconsClicked',
'click .js-deselectAllIcons': '_onDeselectAllIconsClicked',
'click .js-deleteIcons': '_onDeleteIconsClicked',
'change .js-inputFile': '_onFileSelected'
},
initialize: function (options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
this._modals = new ModalsServiceModel();
this.model = new Backbone.Model({
isProcessRunning: false
});
this._iconCollection = new IconCollection(null, {
orgId: this._orgId,
configModel: this._configModel
});
this._numOfUploadingProcesses = 0;
this._numOfDeletingProcesses = 0;
this._fetchErrorMessage = 'Error fetching organization icons. Please refresh the page.';
this._runningMessage = '';
this.render();
this._fetchAllIcons();
this._initBinds();
},
render: function () {
this.$el.html(template());
return this;
},
_initBinds: function () {
this.listenTo(this._iconCollection, 'change:selected', this._refreshActions);
this.listenTo(this.model, 'change:isProcessRunning', this._onProcessRunningChanged);
},
_fetchAllIcons: function () {
this._iconCollection.fetch({
success: this._renderAllIcons.bind(this),
error: this._onFetchIconsError.bind(this)
});
},
_renderAllIcons: function () {
_.each(this._iconCollection.models, this._addIconElement, this);
},
_onFetchIconsError: function () {
this._showErrorMessage(this._fetchErrorMessage);
},
_renderIcon: function (iconModel) {
var iconView = new IconView({
model: iconModel
});
iconView.render();
iconModel.set('visible', true);
this.$('.js-items').append(iconView.$el);
},
_addIconElement: function (iconModel) {
this._renderIcon(iconModel);
},
_onAddIconClicked: function (event) {
this.killEvent(event);
this._hideErrorMessage();
this.$('.js-inputFile').trigger('click');
},
_parseResponseText: function (response) {
if (response && response.responseText) {
try {
var text = JSON.parse(response.responseText);
if (text && text.errors && typeof text.errors === 'string') {
return text.errors;
}
} catch (exc) {
// Swallow
}
}
return '';
},
_getSelectedFiles: function () {
return this.$('.js-inputFile').prop('files');
},
_onFileSelected: function () {
var files = this._getSelectedFiles();
_.each(files, function (file) {
this._iconCollection.create({
kind: 'organization_asset',
resource: file
}, {
beforeSend: this._beforeIconUpload.bind(this),
success: this._onIconUploaded.bind(this),
error: this._onIconUploadError.bind(this),
complete: this._onIconUploadComplete.bind(this)
});
}, this);
},
_beforeIconUpload: function () {
this._numOfUploadingProcesses++;
if (this._numOfUploadingProcesses > 0) {
this._runningMessage = 'Uploading icons...';
this.model.set('isProcessRunning', true);
}
},
_onIconUploaded: function (iconModel) {
this._resetFileSelection();
this._addIconElement(iconModel);
},
_onIconUploadError: function (model, response) {
var errorText = this._parseResponseText(response);
this._resetFileSelection();
this._showErrorMessage(this._uploadErrorMessage(errorText));
},
_onIconUploadComplete: function () {
this._numOfUploadingProcesses--;
if (this._numOfUploadingProcesses <= 0) {
this.model.set('isProcessRunning', false);
}
},
_resetFileSelection: function () {
this.$('.js-inputFile').val('');
},
_show: function (selector) {
this.$(selector).removeClass('is-hidden');
},
_hide: function (selector) {
this.$(selector).addClass('is-hidden');
},
_refreshActions: function () {
if (this.model.get('isProcessRunning')) {
return;
}
var limit = Math.min(this._iconCollection.length);
var numOfSelectedIcons = this._getNumberOfSelectedIcons();
var iconText = (numOfSelectedIcons === 1 ? '1 icon selected' : '' + numOfSelectedIcons + ' icons selected');
this.$('.js-iconMainLabel').text(iconText);
if (numOfSelectedIcons === 0) {
this.$('.js-iconMainLabel').text('');
this._hide('.js-iconMainLabel, .js-selectAllIcons, .js-deselectAllIcons, .js-deleteIcons');
this._show('.js-iconsInfo');
} else if (numOfSelectedIcons < limit) {
this._show('.js-iconMainLabel, .js-selectAllIcons, .js-deleteIcons');
this._hide('.js-deselectAllIcons, .js-iconsInfo');
} else {
this._show('.js-iconMainLabel, .js-deselectAllIcons, .js-deleteIcons');
this._hide('.js-selectAllIcons, .js-iconsInfo');
}
if (numOfSelectedIcons > 1) {
this.$('.js-deleteIcons a').text('Delete icons...');
} else if (numOfSelectedIcons === 1) {
this.$('.js-deleteIcons a').text('Delete icon...');
}
},
_hideActions: function () {
this._hide('.js-selectAllIcons, .js-deselectAllIcons, .js-deleteIcons, .js-iconsInfo');
},
_onDeselectAllIconsClicked: function (event) {
event.preventDefault();
this._iconCollection.each(function (icon) {
icon.set('selected', false);
});
},
_onSelectAllIconsClicked: function (event) {
event.preventDefault();
this._iconCollection.each(function (icon) {
if (icon.get('visible')) {
icon.set('selected', true);
}
});
},
_onDeleteIconsClicked: function (event) {
event.preventDefault();
this._openDeleteIconsDialog();
},
_openDeleteIconsDialog: function (event) {
this.killEvent(event);
this._modals.create(modalModel =>
new DeleteIconsDialog({
modalModel,
numOfIcons: this._getNumberOfSelectedIcons(),
onSubmit: this._deleteIcons.bind(this)
})
);
},
_deleteIcons: function () {
this._hideErrorMessage();
var iconsToDelete = this._iconCollection.where({ selected: true });
_.each(iconsToDelete, function (icon) {
icon.destroy({
beforeSend: this._beforeIconDelete.bind(this),
success: this._onIconDeleted.bind(this),
error: this._onIconDeleteError.bind(this),
complete: this._onIconDeleteComplete.bind(this)
});
}, this);
},
_getNumberOfSelectedIcons: function () {
return this._iconCollection.where({ selected: true }).length;
},
_beforeIconDelete: function () {
this._numOfDeletingProcesses++;
if (this._numOfDeletingProcesses > 0) {
this._runningMessage = 'Deleting icons...';
this.model.set('isProcessRunning', true);
}
},
_onIconDeleted: function (icon) {
icon.set('deleted', true);
this._addExtraIcon();
},
_onIconDeleteError: function (icon, response) {
var errorText = this._parseResponseText(response);
// Even if API throws error the icon has been already removed from the collection.
// We must add it again
this._iconCollection.add(icon);
this._resetSelection();
this._showErrorMessage(this._deleteErrorMessage(errorText));
},
_onIconDeleteComplete: function () {
this._numOfDeletingProcesses--;
if (this._numOfDeletingProcesses <= 0) {
this.model.set('isProcessRunning', false);
}
},
_showSpinner: function (enable) {
if (enable) {
this._hide('.js-plusSign');
this._show('.js-spinner');
} else {
this._show('.js-plusSign');
this._hide('.js-spinner');
}
},
_showErrorMessage: function (message) {
this.$('.js-errorMessage label').text(message);
this._show('.js-errorMessage');
},
_hideErrorMessage: function () {
this._hide('.js-errorMessage');
},
_resetSelection: function () {
this._iconCollection.each(function (icon) {
icon.set('selected', false);
});
this._refreshActions();
},
_addExtraIcon: function () {
var iconAdded = false;
this._iconCollection.each(function (icon) {
if (!icon.get('visible') && !iconAdded) {
this._addIconElement(icon);
iconAdded = true;
}
}, this);
},
_onProcessRunningChanged: function () {
var running = this.model.get('isProcessRunning');
this._showSpinner(running);
if (running) {
this.$el.css('pointer-events', 'none');
this._hideActions();
this.$('.js-runningInfo').text(this._runningMessage);
this._show('.js-runningInfo');
} else {
this.$('.js-runningInfo').text('');
this._hide('.js-runningInfo');
this._refreshActions();
this._iconCollection.trigger('refreshCollection', { cid: this.cid });
this.$el.css('pointer-events', 'auto');
}
},
_uploadErrorMessage: function (errorText) {
var message = 'Error uploading your image. ';
if (errorText) {
message += '[ ' + errorText + ' ]. ';
}
message += 'Please try again.';
return message;
},
_deleteErrorMessage: function (errorText) {
var message = 'Error deleting your image. ';
if (errorText) {
message += '[ ' + errorText + ' ]. ';
}
message += 'Please try again.';
return message;
}
});
@@ -0,0 +1,30 @@
<div class="FormAccount-rowLabel IconActions">
<label class="CDB-Text CDB-Size-medium is-semibold u-mainTextColor u-rSpace--xl js-iconMainLabel is-hidden"></label>
<label class="CDB-Text CDB-Size-small u-rSpace--xl u-altTextColor js-iconsInfo">These icons can be used as markers.</label>
<label class="CDB-Text CDB-Size-small u-rSpace--xl u-altTextColor js-runningInfo is-hidden"></label>
<label class="CDB-Text CDB-Size-medium u-rSpace--xl js-selectAllIcons is-hidden"><a href="#/select_all">Select all</a></label>
<label class="CDB-Text CDB-Size-medium u-rSpace--xl js-deselectAllIcons is-hidden"><a href="#/deselect_all">Deselect all</a></label>
<label class="CDB-Text CDB-Size-medium js-deleteIcons is-hidden"><a class="IconText is--critical" href="#/delete_icons">Delete icons...</a></label>
</div>
<div class="FormAccount-rowData js-asset-icons">
<ul class="IconList js-items">
<li class="IconList-item IconList-item--small IconList-item--dashed js-addIcon">
<div class="IconItem-icon js-plusSign">
<svg width="11px" height="11px" viewBox="15 15 11 11" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<polygon id="Mas" stroke="none" fill="#1785FB" fill-rule="evenodd" points="15 20 15 21 20 21 20 26 21 26 21 21 26 21 26 20 21 20 21 15 20 15 20 20"></polygon>
</svg>
</div>
<div class="CDB-LoaderIcon is-blue js-spinner is-hidden">
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
</svg>
</div>
</li>
</ul>
</div>
<div class="FormAccount-rowData js-errorMessage is-hidden">
<label class="CDB-Text CDB-Size-medium IconText is--critical"></label>
</div>
<input id="iconfile" class="js-inputFile" type="file" value="Choose icon" accept="image/jpeg,image/jpg,image/gif,image/png,image/svg+xml" tabindex="-1" style="position: absolute; clip: rect(0px 0px 0px 0px); opacity: 0;" multiple>
@@ -0,0 +1,143 @@
const Backbone = require('backbone');
const IconPickerView = require('./icons/organization-icons-view');
const OrganizationIconCollection = require('./icons/organization-icon-collection');
const IconView = require('./icons/organization-icon-view');
const IconPickerDialog = require('./icon-picker-dialog/icon-picker-dialog-view');
const ModalsServiceModel = require('builder/components/modals/modals-service-model');
const template = require('./organization-icon-picker.tpl');
const checkAndBuildOpts = require('builder/helpers/required-opts');
const REQUIRED_OPTS = [
'orgId',
'configModel'
];
module.exports = IconPickerView.extend({
events: IconPickerView.extendEvents({
'click .js-viewAllIcons': '_onViewAllIconsClicked'
}),
initialize: function (options) {
checkAndBuildOpts(options, REQUIRED_OPTS, this);
this._maxIcons = 23;
this._modals = new ModalsServiceModel();
this.model = new Backbone.Model({
isProcessRunning: false
});
this._iconCollection = new OrganizationIconCollection(null, {
orgId: this._orgId,
configModel: this._configModel
});
this._numOfUploadingProcesses = 0;
this._numOfDeletingProcesses = 0;
this._fetchErrorMessage = 'Error fetching organization icons. Please refresh the page.';
this._runningMessage = '';
this.teplate = template;
this.render();
this._fetchAllIcons();
this._initBinds();
},
render: function () {
this.$el.html(template());
return this;
},
_initBinds: function () {
this.listenTo(this._iconCollection, 'change:selected', this._refreshActions);
this.listenTo(this._iconCollection, 'refreshCollection', this._refreshCollection);
this.listenTo(this.model, 'change:isProcessRunning', this._onProcessRunningChanged);
},
_refreshCollection: function (data) {
if (data.cid && this.cid !== data.cid) {
this.render();
this._fetchAllIcons();
}
},
_renderIcon: function (iconModel) {
if (iconModel.get('index') < this._maxIcons) {
var iconView = new IconView({
model: iconModel
});
iconView.render();
iconModel.set('visible', true);
this.$('.js-items').append(iconView.$el);
}
},
_addIconElement: function (iconModel) {
iconModel.set('index', this._getIconIndex(iconModel));
this._renderIcon(iconModel);
this._refreshActions();
},
_refreshActions: function () {
if (this.model.get('isProcessRunning')) {
return;
}
var limit = Math.min(this._maxIcons, this._iconCollection.length);
var numOfSelectedIcons = this._getNumberOfSelectedIcons();
var iconText = (numOfSelectedIcons === 1 ? '1 icon selected' : '' + numOfSelectedIcons + ' icons selected');
this.$('.js-iconMainLabel').text(iconText);
if (numOfSelectedIcons === 0) {
this.$('.js-iconMainLabel').text('Icons');
this._hide('.js-selectAllIcons, .js-deselectAllIcons, .js-deleteIcons');
this._show('.js-iconsInfo');
} else if (numOfSelectedIcons < limit) {
this._show('.js-selectAllIcons, .js-deleteIcons');
this._hide('.js-deselectAllIcons, .js-iconsInfo');
} else {
this._hide('.js-selectAllIcons, .js-iconsInfo');
this._show('.js-deselectAllIcons, .js-deleteIcons');
}
if (numOfSelectedIcons > 1) {
this.$('.js-deleteIcons a').text('Delete icons...');
} else if (numOfSelectedIcons === 1) {
this.$('.js-deleteIcons a').text('Delete icon...');
}
if (this._iconCollection.length > this._maxIcons) {
this._show('.js-viewAllIcons');
} else {
this._hide('.js-viewAllIcons');
}
},
_hideActions: function () {
this._hide('.js-selectAllIcons, .js-deselectAllIcons, .js-deleteIcons, .js-iconsInfo, .js-viewAllIcons');
},
_getIconIndex: function (icon) {
return this._iconCollection.indexOf(icon);
},
_addExtraIcon: function () {
var iconAdded = false;
this._iconCollection.each(function (icon) {
var index = this._getIconIndex(icon);
if (index < this._maxIcons && !icon.get('visible') && !iconAdded) {
this._addIconElement(icon);
iconAdded = true;
}
}, this);
},
_onViewAllIconsClicked: function (event) {
this.killEvent(event);
this._modals.create(modalModel =>
new IconPickerDialog({
orgId: this._orgId,
configModel: this._configModel
})
);
}
});
@@ -0,0 +1,31 @@
<div class="FormAccount-rowLabel">
<label class="CDB-Text CDB-Size-medium is-semibold u-mainTextColor u-rSpace--xl js-iconMainLabel">Icons</label>
<label class="CDB-Text CDB-Size-medium u-rSpace--xl js-viewAllIcons is-hidden"><a href="#/view_icons">View all icons</a></label>
<label class="CDB-Text CDB-Size-small u-rSpace--xl u-altTextColor js-iconsInfo">These icons can be used as markers.</label>
<label class="CDB-Text CDB-Size-small u-rSpace--xl u-altTextColor js-runningInfo is-hidden"></label>
<label class="CDB-Text CDB-Size-medium u-rSpace--xl js-selectAllIcons is-hidden"><a href="#/select_all">Select all</a></label>
<label class="CDB-Text CDB-Size-medium u-rSpace--xl js-deselectAllIcons is-hidden"><a href="#/deselect_all">Deselect all</a></label>
<label class="CDB-Text CDB-Size-medium js-deleteIcons is-hidden"><a class="IconText is--critical" href="#/delete_icons">Delete icons...</a></label>
</div>
<div class="FormAccount-rowData js-asset-icons">
<ul class="IconList js-items">
<li class="IconList-item IconList-item--small IconList-item--dashed js-addIcon">
<div class="IconItem-icon js-plusSign">
<svg width="11px" height="11px" viewBox="15 15 11 11" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<polygon id="Mas" stroke="none" fill="#1785FB" fill-rule="evenodd" points="15 20 15 21 20 21 20 26 21 26 21 21 26 21 26 20 21 20 21 15 20 15 20 20"></polygon>
</svg>
</div>
<div class="CDB-LoaderIcon is-blue js-spinner is-hidden">
<svg class="CDB-LoaderIcon-spinner" viewBox="0 0 50 50">
<circle class="CDB-LoaderIcon-path" cx="25" cy="25" r="20" fill="none"></circle>
</svg>
</div>
</li>
</ul>
</div>
<div class="FormAccount-rowData js-errorMessage is-hidden">
<label class="CDB-Text CDB-Size-medium IconText is--critical"></label>
</div>
<input id="iconfile" class="js-inputFile" type="file" value="Choose icon" accept="image/jpeg,image/jpg,image/gif,image/png,image/svg+xml" tabindex="-1" style="position: absolute; clip: rect(0px 0px 0px 0px); opacity: 0;" multiple>