├── .editorconfig ├── .env.example ├── .flaskenv ├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── Dockerfile ├── Dockerfile.multi-stage ├── LICENSE ├── README.md ├── app.py ├── demo.png ├── docker-compose.yaml ├── docker-entrypoint.sh ├── greybook ├── __init__.py ├── blueprints │ ├── __init__.py │ ├── admin.py │ ├── auth.py │ └── blog.py ├── core │ ├── __init__.py │ ├── commands.py │ ├── errors.py │ ├── extensions.py │ ├── logging.py │ ├── request.py │ └── templating.py ├── emails.py ├── forms.py ├── lorem.py ├── models.py ├── settings.py ├── static │ ├── ckeditor │ │ ├── CHANGES.md │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── adapters │ │ │ └── jquery.js │ │ ├── build-config.js │ │ ├── ckeditor.js │ │ ├── config.js │ │ ├── contents.css │ │ ├── lang │ │ │ ├── af.js │ │ │ ├── ar.js │ │ │ ├── az.js │ │ │ ├── bg.js │ │ │ ├── bn.js │ │ │ ├── bs.js │ │ │ ├── ca.js │ │ │ ├── cs.js │ │ │ ├── cy.js │ │ │ ├── da.js │ │ │ ├── de-ch.js │ │ │ ├── de.js │ │ │ ├── el.js │ │ │ ├── en-au.js │ │ │ ├── en-ca.js │ │ │ ├── en-gb.js │ │ │ ├── en.js │ │ │ ├── eo.js │ │ │ ├── es-mx.js │ │ │ ├── es.js │ │ │ ├── et.js │ │ │ ├── eu.js │ │ │ ├── fa.js │ │ │ ├── fi.js │ │ │ ├── fo.js │ │ │ ├── fr-ca.js │ │ │ ├── fr.js │ │ │ ├── gl.js │ │ │ ├── gu.js │ │ │ ├── he.js │ │ │ ├── hi.js │ │ │ ├── hr.js │ │ │ ├── hu.js │ │ │ ├── id.js │ │ │ ├── is.js │ │ │ ├── it.js │ │ │ ├── ja.js │ │ │ ├── ka.js │ │ │ ├── km.js │ │ │ ├── ko.js │ │ │ ├── ku.js │ │ │ ├── lt.js │ │ │ ├── lv.js │ │ │ ├── mk.js │ │ │ ├── mn.js │ │ │ ├── ms.js │ │ │ ├── nb.js │ │ │ ├── nl.js │ │ │ ├── no.js │ │ │ ├── oc.js │ │ │ ├── pl.js │ │ │ ├── pt-br.js │ │ │ ├── pt.js │ │ │ ├── ro.js │ │ │ ├── ru.js │ │ │ ├── si.js │ │ │ ├── sk.js │ │ │ ├── sl.js │ │ │ ├── sq.js │ │ │ ├── sr-latn.js │ │ │ ├── sr.js │ │ │ ├── sv.js │ │ │ ├── th.js │ │ │ ├── tr.js │ │ │ ├── tt.js │ │ │ ├── ug.js │ │ │ ├── uk.js │ │ │ ├── vi.js │ │ │ ├── zh-cn.js │ │ │ └── zh.js │ │ ├── plugins │ │ │ ├── a11yhelp │ │ │ │ └── dialogs │ │ │ │ │ ├── a11yhelp.js │ │ │ │ │ └── lang │ │ │ │ │ ├── _translationstatus.txt │ │ │ │ │ ├── af.js │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── az.js │ │ │ │ │ ├── bg.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── cy.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de-ch.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── el.js │ │ │ │ │ ├── en-au.js │ │ │ │ │ ├── en-gb.js │ │ │ │ │ ├── en.js │ │ │ │ │ ├── eo.js │ │ │ │ │ ├── es-mx.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── et.js │ │ │ │ │ ├── eu.js │ │ │ │ │ ├── fa.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── fo.js │ │ │ │ │ ├── fr-ca.js │ │ │ │ │ ├── fr.js │ │ │ │ │ ├── gl.js │ │ │ │ │ ├── gu.js │ │ │ │ │ ├── he.js │ │ │ │ │ ├── hi.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── km.js │ │ │ │ │ ├── ko.js │ │ │ │ │ ├── ku.js │ │ │ │ │ ├── lt.js │ │ │ │ │ ├── lv.js │ │ │ │ │ ├── mk.js │ │ │ │ │ ├── mn.js │ │ │ │ │ ├── nb.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── no.js │ │ │ │ │ ├── oc.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt-br.js │ │ │ │ │ ├── pt.js │ │ │ │ │ ├── ro.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── si.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sl.js │ │ │ │ │ ├── sq.js │ │ │ │ │ ├── sr-latn.js │ │ │ │ │ ├── sr.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── th.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── tt.js │ │ │ │ │ ├── ug.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ └── zh.js │ │ │ ├── about │ │ │ │ └── dialogs │ │ │ │ │ ├── about.js │ │ │ │ │ ├── hidpi │ │ │ │ │ └── logo_ckeditor.png │ │ │ │ │ └── logo_ckeditor.png │ │ │ ├── clipboard │ │ │ │ └── dialogs │ │ │ │ │ └── paste.js │ │ │ ├── codesnippet │ │ │ │ ├── dialogs │ │ │ │ │ └── codesnippet.js │ │ │ │ ├── icons │ │ │ │ │ ├── codesnippet.png │ │ │ │ │ └── hidpi │ │ │ │ │ │ └── codesnippet.png │ │ │ │ ├── lang │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── az.js │ │ │ │ │ ├── bg.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de-ch.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── el.js │ │ │ │ │ ├── en-au.js │ │ │ │ │ ├── en-gb.js │ │ │ │ │ ├── en.js │ │ │ │ │ ├── eo.js │ │ │ │ │ ├── es-mx.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── et.js │ │ │ │ │ ├── eu.js │ │ │ │ │ ├── fa.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── fr-ca.js │ │ │ │ │ ├── fr.js │ │ │ │ │ ├── gl.js │ │ │ │ │ ├── he.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── km.js │ │ │ │ │ ├── ko.js │ │ │ │ │ ├── ku.js │ │ │ │ │ ├── lt.js │ │ │ │ │ ├── lv.js │ │ │ │ │ ├── nb.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── no.js │ │ │ │ │ ├── oc.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt-br.js │ │ │ │ │ ├── pt.js │ │ │ │ │ ├── ro.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sl.js │ │ │ │ │ ├── sq.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── th.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── tt.js │ │ │ │ │ ├── ug.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ └── zh.js │ │ │ │ ├── plugin.js │ │ │ │ └── samples │ │ │ │ │ └── codesnippet.html │ │ │ ├── dialog │ │ │ │ └── dialogDefinition.js │ │ │ ├── filebrowser │ │ │ │ └── plugin.js │ │ │ ├── filetools │ │ │ │ ├── dev │ │ │ │ │ └── uploaddebugger.js │ │ │ │ ├── lang │ │ │ │ │ ├── az.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de-ch.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── en-au.js │ │ │ │ │ ├── en.js │ │ │ │ │ ├── eo.js │ │ │ │ │ ├── es-mx.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── eu.js │ │ │ │ │ ├── fr.js │ │ │ │ │ ├── gl.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── km.js │ │ │ │ │ ├── ko.js │ │ │ │ │ ├── ku.js │ │ │ │ │ ├── nb.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── oc.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt-br.js │ │ │ │ │ ├── pt.js │ │ │ │ │ ├── ro.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sq.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── ug.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ └── zh.js │ │ │ │ └── plugin.js │ │ │ ├── icons.png │ │ │ ├── icons_hidpi.png │ │ │ ├── image │ │ │ │ ├── dialogs │ │ │ │ │ └── image.js │ │ │ │ └── images │ │ │ │ │ └── noimage.png │ │ │ ├── link │ │ │ │ ├── dialogs │ │ │ │ │ ├── anchor.js │ │ │ │ │ └── link.js │ │ │ │ └── images │ │ │ │ │ ├── anchor.png │ │ │ │ │ └── hidpi │ │ │ │ │ └── anchor.png │ │ │ ├── magicline │ │ │ │ └── images │ │ │ │ │ ├── hidpi │ │ │ │ │ ├── icon-rtl.png │ │ │ │ │ └── icon.png │ │ │ │ │ ├── icon-rtl.png │ │ │ │ │ └── icon.png │ │ │ ├── pastefromword │ │ │ │ └── filter │ │ │ │ │ └── default.js │ │ │ ├── popup │ │ │ │ └── plugin.js │ │ │ ├── scayt │ │ │ │ ├── CHANGELOG.md │ │ │ │ ├── LICENSE.md │ │ │ │ ├── README.md │ │ │ │ ├── dialogs │ │ │ │ │ ├── dialog.css │ │ │ │ │ ├── options.js │ │ │ │ │ └── toolbar.css │ │ │ │ └── skins │ │ │ │ │ └── moono-lisa │ │ │ │ │ └── scayt.css │ │ │ ├── specialchar │ │ │ │ └── dialogs │ │ │ │ │ ├── lang │ │ │ │ │ ├── _translationstatus.txt │ │ │ │ │ ├── af.js │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── az.js │ │ │ │ │ ├── bg.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── cy.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de-ch.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── el.js │ │ │ │ │ ├── en-au.js │ │ │ │ │ ├── en-ca.js │ │ │ │ │ ├── en-gb.js │ │ │ │ │ ├── en.js │ │ │ │ │ ├── eo.js │ │ │ │ │ ├── es-mx.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── et.js │ │ │ │ │ ├── eu.js │ │ │ │ │ ├── fa.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── fr-ca.js │ │ │ │ │ ├── fr.js │ │ │ │ │ ├── gl.js │ │ │ │ │ ├── he.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── km.js │ │ │ │ │ ├── ko.js │ │ │ │ │ ├── ku.js │ │ │ │ │ ├── lt.js │ │ │ │ │ ├── lv.js │ │ │ │ │ ├── nb.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── no.js │ │ │ │ │ ├── oc.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt-br.js │ │ │ │ │ ├── pt.js │ │ │ │ │ ├── ro.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── si.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sl.js │ │ │ │ │ ├── sq.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── th.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── tt.js │ │ │ │ │ ├── ug.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ └── zh.js │ │ │ │ │ └── specialchar.js │ │ │ ├── table │ │ │ │ └── dialogs │ │ │ │ │ └── table.js │ │ │ ├── tableselection │ │ │ │ └── styles │ │ │ │ │ └── tableselection.css │ │ │ ├── tabletools │ │ │ │ └── dialogs │ │ │ │ │ └── tableCell.js │ │ │ ├── widget │ │ │ │ └── images │ │ │ │ │ └── handle.png │ │ │ └── wsc │ │ │ │ ├── LICENSE.md │ │ │ │ ├── README.md │ │ │ │ ├── dialogs │ │ │ │ ├── ciframe.html │ │ │ │ ├── tmpFrameset.html │ │ │ │ ├── wsc.css │ │ │ │ ├── wsc.js │ │ │ │ └── wsc_ie.js │ │ │ │ └── skins │ │ │ │ └── moono-lisa │ │ │ │ └── wsc.css │ │ ├── samples │ │ │ ├── css │ │ │ │ └── samples.css │ │ │ ├── img │ │ │ │ ├── github-top.png │ │ │ │ ├── header-bg.png │ │ │ │ ├── header-separator.png │ │ │ │ ├── logo.png │ │ │ │ ├── logo.svg │ │ │ │ └── navigation-tip.png │ │ │ ├── index.html │ │ │ ├── js │ │ │ │ ├── sample.js │ │ │ │ └── sf.js │ │ │ ├── old │ │ │ │ ├── ajax.html │ │ │ │ ├── api.html │ │ │ │ ├── appendto.html │ │ │ │ ├── assets │ │ │ │ │ ├── inlineall │ │ │ │ │ │ └── logo.png │ │ │ │ │ ├── outputxhtml │ │ │ │ │ │ └── outputxhtml.css │ │ │ │ │ ├── posteddata.php │ │ │ │ │ ├── sample.jpg │ │ │ │ │ └── uilanguages │ │ │ │ │ │ └── languages.js │ │ │ │ ├── datafiltering.html │ │ │ │ ├── dialog │ │ │ │ │ ├── assets │ │ │ │ │ │ └── my_dialog.js │ │ │ │ │ └── dialog.html │ │ │ │ ├── divreplace.html │ │ │ │ ├── enterkey │ │ │ │ │ └── enterkey.html │ │ │ │ ├── htmlwriter │ │ │ │ │ ├── assets │ │ │ │ │ │ └── outputforflash │ │ │ │ │ │ │ ├── outputforflash.fla │ │ │ │ │ │ │ ├── outputforflash.swf │ │ │ │ │ │ │ └── swfobject.js │ │ │ │ │ ├── outputforflash.html │ │ │ │ │ └── outputhtml.html │ │ │ │ ├── index.html │ │ │ │ ├── inlineall.html │ │ │ │ ├── inlinebycode.html │ │ │ │ ├── inlinetextarea.html │ │ │ │ ├── jquery.html │ │ │ │ ├── magicline │ │ │ │ │ └── magicline.html │ │ │ │ ├── readonly.html │ │ │ │ ├── replacebyclass.html │ │ │ │ ├── replacebycode.html │ │ │ │ ├── sample.css │ │ │ │ ├── sample.js │ │ │ │ ├── sample_posteddata.php │ │ │ │ ├── tabindex.html │ │ │ │ ├── toolbar │ │ │ │ │ └── toolbar.html │ │ │ │ ├── uicolor.html │ │ │ │ ├── uilanguages.html │ │ │ │ ├── wysiwygarea │ │ │ │ │ └── fullpage.html │ │ │ │ └── xhtmlstyle.html │ │ │ └── toolbarconfigurator │ │ │ │ ├── css │ │ │ │ └── fontello.css │ │ │ │ ├── font │ │ │ │ ├── LICENSE.txt │ │ │ │ ├── config.json │ │ │ │ ├── fontello.eot │ │ │ │ ├── fontello.svg │ │ │ │ ├── fontello.ttf │ │ │ │ └── fontello.woff │ │ │ │ ├── index.html │ │ │ │ └── js │ │ │ │ ├── abstracttoolbarmodifier.js │ │ │ │ ├── fulltoolbareditor.js │ │ │ │ ├── toolbarmodifier.js │ │ │ │ └── toolbartextmodifier.js │ │ ├── skins │ │ │ └── moono-lisa │ │ │ │ ├── dialog.css │ │ │ │ ├── dialog_ie.css │ │ │ │ ├── dialog_ie8.css │ │ │ │ ├── dialog_iequirks.css │ │ │ │ ├── editor.css │ │ │ │ ├── editor_gecko.css │ │ │ │ ├── editor_ie.css │ │ │ │ ├── editor_ie8.css │ │ │ │ ├── editor_iequirks.css │ │ │ │ ├── icons.png │ │ │ │ ├── icons_hidpi.png │ │ │ │ ├── images │ │ │ │ ├── arrow.png │ │ │ │ ├── close.png │ │ │ │ ├── hidpi │ │ │ │ │ ├── close.png │ │ │ │ │ ├── lock-open.png │ │ │ │ │ ├── lock.png │ │ │ │ │ └── refresh.png │ │ │ │ ├── lock-open.png │ │ │ │ ├── lock.png │ │ │ │ ├── refresh.png │ │ │ │ └── spinner.gif │ │ │ │ └── readme.md │ │ └── styles.js │ ├── css │ │ ├── bluelog.min.css │ │ ├── default.min.css │ │ └── main.css │ ├── favicon.ico │ └── js │ │ ├── bootstrap.bundle.min.js │ │ ├── dayjs │ │ ├── dayjs.min.js │ │ └── plugin │ │ │ ├── customParseFormat.js │ │ │ ├── localizedFormat.js │ │ │ ├── relativeTime.js │ │ │ └── utc.js │ │ └── main.js ├── templates │ ├── admin │ │ ├── edit_category.html │ │ ├── edit_link.html │ │ ├── edit_post.html │ │ ├── manage_category.html │ │ ├── manage_comment.html │ │ ├── manage_link.html │ │ ├── manage_post.html │ │ ├── new_category.html │ │ ├── new_link.html │ │ ├── new_post.html │ │ └── settings.html │ ├── auth │ │ └── login.html │ ├── base.html │ ├── blog │ │ ├── _posts.html │ │ ├── _sidebar.html │ │ ├── about.html │ │ ├── category.html │ │ ├── index.html │ │ └── post.html │ ├── errors │ │ ├── 400.html │ │ ├── 404.html │ │ └── 500.html │ └── macros.html └── utils.py ├── logs └── .gitkeep ├── migrations ├── README ├── alembic.ini ├── env.py ├── script.py.mako └── versions │ └── 5c7fb996a88b_initial_migration.py ├── pdm.lock ├── pyproject.toml ├── requirements.txt ├── tests ├── __init__.py ├── test_admin.py ├── test_auth.py ├── test_basic.py ├── test_blog.py └── test_cli.py └── uploads └── .gitkeep /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | charset = utf-8 10 | max_line_length = 100 11 | 12 | [*.{yml,yaml,json,js,css,html}] 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # FLASK_CONFIG=production # enable for production 2 | # SECRET_KEY=your-secret-key 3 | # DATABASE_URL=sqlite:////home/greybook/database/data.db 4 | # MAIL_SERVER=smtp.example.com 5 | # MAIL_USERNAME=example 6 | # MAIL_PASSWORD=example-password -------------------------------------------------------------------------------- /.flaskenv: -------------------------------------------------------------------------------- 1 | FLASK_APP=app 2 | FLASK_DEBUG=1 3 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths-ignore: 7 | - '*.md' 8 | pull_request: 9 | branches: 10 | - main 11 | paths-ignore: 12 | - '*.md' 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v4 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | - name: Install dependencies 26 | run: | 27 | python -m pip install --upgrade pip 28 | pip install pytest 29 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 30 | - name: Test with pytest 31 | run: | 32 | pytest -v 33 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/astral-sh/ruff-pre-commit 3 | rev: v0.4.4 4 | hooks: 5 | - id: ruff 6 | args: [ --fix ] 7 | - id: ruff-format 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-slim 2 | 3 | RUN groupadd -r greybook && useradd -r -g greybook greybook 4 | 5 | WORKDIR /home/greybook 6 | 7 | RUN pip install -U pdm 8 | ENV PDM_CHECK_UPDATE=false 9 | COPY pyproject.toml pdm.lock ./ 10 | RUN pdm install --check --prod --no-editable 11 | ENV PATH="/home/greybook/.venv/bin:$PATH" 12 | 13 | COPY greybook greybook 14 | COPY migrations migrations 15 | COPY uploads uploads 16 | COPY app.py docker-entrypoint.sh ./ 17 | 18 | RUN chown -R greybook:greybook . 19 | USER greybook 20 | 21 | ENV FLASK_APP=app.py 22 | ENV FLASK_CONFIG=production 23 | ENV GREYBOOK_LOGGING_PATH=stream 24 | 25 | EXPOSE 5000 26 | ENTRYPOINT ["./docker-entrypoint.sh"] 27 | -------------------------------------------------------------------------------- /Dockerfile.multi-stage: -------------------------------------------------------------------------------- 1 | # stage 1: build 2 | ARG base_image=python:3.12-slim 3 | FROM ${base_image} AS build 4 | 5 | WORKDIR /home/greybook 6 | 7 | # install dependencies 8 | RUN pip install -U pdm 9 | ENV PDM_CHECK_UPDATE=false 10 | COPY pyproject.toml pdm.lock ./ 11 | RUN pdm install --check --prod --no-editable 12 | 13 | # stage 2: production 14 | FROM ${base_image} 15 | 16 | RUN groupadd -r greybook && useradd -r -g greybook greybook 17 | 18 | WORKDIR /home/greybook 19 | 20 | # copy the installed dependencies from the previous stage 21 | COPY --from=build /home/greybook/.venv/ .venv/ 22 | ENV PATH="/home/greybook/.venv/bin:$PATH" 23 | 24 | # copy the application source code from the previous stage 25 | COPY greybook greybook 26 | COPY migrations migrations 27 | COPY uploads uploads 28 | COPY app.py . 29 | 30 | RUN chown -R greybook:greybook . 31 | USER greybook 32 | 33 | ENV FLASK_APP=app.py 34 | ENV FLASK_CONFIG=production 35 | ENV GREYBOOK_LOGGING_PATH=stream 36 | 37 | EXPOSE 5000 38 | ENTRYPOINT ["./docker-entrypoint.sh"] 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Grey Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Greybook 2 | 3 | A blog engine built with Python and Flask. The example application for the book *[Python Web Development with Flask (2nd edition)](https://helloflask.com/en/book/4)* (《[Flask Web 开发实战(第 2 版)](https://helloflask.com/book/4)》). 4 | 5 | Demo: http://greybook.helloflask.com 6 | 7 | ![Screenshot](demo.png) 8 | 9 | ## Installation 10 | 11 | Clone the repo: 12 | 13 | ``` 14 | $ git clone https://github.com/greyli/greybook 15 | $ cd greybook 16 | ``` 17 | 18 | Install dependencies with [PDM](https://pdm.fming.dev): 19 | 20 | ``` 21 | $ pdm install 22 | ``` 23 | 24 | > [!TIP] 25 | > If you don't have PDM installed, you can create a virtual environment with `venv` and install dependencies with `pip install -r requirements.txt`. 26 | 27 | To initialize the app, run the `flask init-blog` command: 28 | 29 | ``` 30 | $ pdm run flask init-blog 31 | ``` 32 | 33 | If you just want to try it out, generate fake data with `flask lorem` command: 34 | 35 | ``` 36 | $ pdm run flask lorem 37 | ``` 38 | 39 | It will create a test account: 40 | 41 | * username: `admin` 42 | * password: `greybook` 43 | 44 | Now you can run the app: 45 | 46 | ``` 47 | $ pdm run flask run 48 | * Running on http://127.0.0.1:5000/ 49 | ``` 50 | 51 | ## License 52 | 53 | This project is licensed under the MIT License (see the 54 | [LICENSE](LICENSE) file for details). 55 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | from dotenv import load_dotenv 5 | 6 | dotenv_path = Path(__file__).resolve().parent / '.env' 7 | if dotenv_path.exists(): 8 | load_dotenv(dotenv_path) 9 | 10 | from greybook import create_app # noqa 11 | 12 | config_name = os.getenv('FLASK_CONFIG', 'development') 13 | app = create_app(config_name) 14 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/demo.png -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | greybook: 3 | build: 4 | context: . 5 | dockerfile: Dockerfile 6 | env_file: 7 | - .env 8 | ports: 9 | - "8000:5000" 10 | volumes: 11 | - greybook-database:/home/greybook/database 12 | - greybook-uploads:/home/greybook/uploads 13 | 14 | volumes: 15 | greybook-database: 16 | greybook-uploads: 17 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # run database migrations 5 | flask db upgrade 6 | 7 | # start Gunicorn 8 | exec gunicorn --workers 4 --bind 0.0.0.0:5000 --access-logfile - --error-logfile - app:app 9 | -------------------------------------------------------------------------------- /greybook/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | from greybook.blueprints.admin import admin_bp 4 | from greybook.blueprints.auth import auth_bp 5 | from greybook.blueprints.blog import blog_bp 6 | from greybook.core.commands import register_commands 7 | from greybook.core.errors import register_errors 8 | from greybook.core.extensions import bootstrap, ckeditor, csrf, db, login_manager, mail, migrate, toolbar 9 | from greybook.core.logging import register_logging 10 | from greybook.core.request import register_request_handlers 11 | from greybook.core.templating import register_template_handlers 12 | from greybook.settings import config 13 | 14 | 15 | def create_app(config_name): 16 | app = Flask('greybook') 17 | app.config.from_object(config[config_name]) 18 | 19 | # blueprints 20 | app.register_blueprint(blog_bp) 21 | app.register_blueprint(admin_bp, url_prefix='/admin') 22 | app.register_blueprint(auth_bp, url_prefix='/auth') 23 | 24 | # extensions 25 | bootstrap.init_app(app) 26 | db.init_app(app) 27 | login_manager.init_app(app) 28 | csrf.init_app(app) 29 | ckeditor.init_app(app) 30 | mail.init_app(app) 31 | toolbar.init_app(app) 32 | migrate.init_app(app, db) 33 | 34 | register_logging(app) 35 | register_commands(app) 36 | register_errors(app) 37 | register_template_handlers(app) 38 | register_request_handlers(app) 39 | 40 | return app 41 | -------------------------------------------------------------------------------- /greybook/blueprints/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/blueprints/__init__.py -------------------------------------------------------------------------------- /greybook/blueprints/auth.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, flash, redirect, render_template, url_for 2 | from flask_login import current_user, login_required, login_user, logout_user 3 | from sqlalchemy import select 4 | 5 | from greybook.core.extensions import db 6 | from greybook.forms import LoginForm 7 | from greybook.models import Admin 8 | from greybook.utils import redirect_back 9 | 10 | auth_bp = Blueprint('auth', __name__) 11 | 12 | 13 | @auth_bp.route('/login', methods=['GET', 'POST']) 14 | def login(): 15 | if current_user.is_authenticated: 16 | return redirect(url_for('blog.index')) 17 | 18 | form = LoginForm() 19 | if form.validate_on_submit(): 20 | username = form.username.data 21 | password = form.password.data 22 | remember = form.remember.data 23 | admin = db.session.scalar(select(Admin)) 24 | if admin: 25 | if username == admin.username and admin.validate_password(password): 26 | login_user(admin, remember) 27 | flash('Welcome back.', 'info') 28 | return redirect_back() 29 | flash('Invalid username or password.', 'warning') 30 | else: 31 | flash('No account found.', 'warning') 32 | return render_template('auth/login.html', form=form) 33 | 34 | 35 | @auth_bp.route('/logout') 36 | @login_required 37 | def logout(): 38 | logout_user() 39 | flash('Logout success.', 'info') 40 | return redirect_back() 41 | -------------------------------------------------------------------------------- /greybook/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/core/__init__.py -------------------------------------------------------------------------------- /greybook/core/errors.py: -------------------------------------------------------------------------------- 1 | from flask import render_template 2 | from flask_wtf.csrf import CSRFError 3 | 4 | 5 | def register_errors(app): 6 | @app.errorhandler(400) 7 | def bad_request(error): 8 | return render_template('errors/400.html', description=error.description), 400 9 | 10 | @app.errorhandler(404) 11 | def page_not_found(error): 12 | return render_template('errors/404.html', description=error.description), 404 13 | 14 | @app.errorhandler(500) 15 | def internal_server_error(error): 16 | return render_template('errors/500.html', description=error.description), 500 17 | 18 | @app.errorhandler(CSRFError) 19 | def handle_csrf_error(error): 20 | description = 'Session expired, return last page and try again.' 21 | return render_template('errors/400.html', description=description), 400 22 | -------------------------------------------------------------------------------- /greybook/core/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_bootstrap import Bootstrap5 2 | from flask_ckeditor import CKEditor 3 | from flask_debugtoolbar import DebugToolbarExtension 4 | from flask_login import LoginManager 5 | from flask_mailman import Mail 6 | from flask_migrate import Migrate 7 | from flask_sqlalchemy import SQLAlchemy 8 | from flask_wtf import CSRFProtect 9 | from sqlalchemy import MetaData 10 | from sqlalchemy.orm import DeclarativeBase 11 | 12 | 13 | class Base(DeclarativeBase): 14 | metadata = MetaData( 15 | naming_convention={ 16 | 'ix': 'ix_%(column_0_label)s', 17 | 'uq': 'uq_%(table_name)s_%(column_0_name)s', 18 | 'ck': 'ck_%(table_name)s_%(constraint_name)s', 19 | 'fk': 'fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s', 20 | 'pk': 'pk_%(table_name)s', 21 | } 22 | ) 23 | 24 | 25 | bootstrap = Bootstrap5() 26 | db = SQLAlchemy(model_class=Base) 27 | login_manager = LoginManager() 28 | csrf = CSRFProtect() 29 | ckeditor = CKEditor() 30 | mail = Mail() 31 | toolbar = DebugToolbarExtension() 32 | migrate = Migrate() 33 | 34 | 35 | @login_manager.user_loader 36 | def load_user(user_id): 37 | from greybook.models import Admin 38 | 39 | user = db.session.get(Admin, int(user_id)) 40 | return user 41 | 42 | 43 | login_manager.login_view = 'auth.login' 44 | # login_manager.login_message = 'Your custom message' 45 | login_manager.login_message_category = 'warning' 46 | -------------------------------------------------------------------------------- /greybook/core/logging.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from logging.handlers import RotatingFileHandler, SMTPHandler 3 | 4 | from flask import request 5 | from flask.logging import wsgi_errors_stream 6 | 7 | 8 | def register_logging(app): 9 | formatter = logging.Formatter('[%(asctime)s] %(levelname)s in %(module)s: %(message)s') 10 | 11 | class RequestFormatter(logging.Formatter): 12 | def format(self, record): 13 | record.url = request.url 14 | record.remote_addr = request.remote_addr 15 | return super().format(record) 16 | 17 | request_formatter = RequestFormatter( 18 | '[%(asctime)s] %(remote_addr)s requested %(url)s\n' '%(levelname)s in %(module)s: %(message)s' 19 | ) 20 | 21 | logging_path = app.config['GREYBOOK_LOGGING_PATH'] 22 | if logging_path == 'stream': 23 | file_handler = logging.StreamHandler(wsgi_errors_stream) 24 | else: 25 | file_handler = RotatingFileHandler(logging_path, maxBytes=10 * 1024 * 1024, backupCount=10) 26 | file_handler.setFormatter(formatter) 27 | file_handler.setLevel(logging.INFO) 28 | 29 | mail_handler = SMTPHandler( 30 | mailhost=app.config['MAIL_SERVER'], 31 | fromaddr=app.config['MAIL_USERNAME'], 32 | toaddrs=['GREYBOOK_ADMIN_EMAIL'], 33 | subject=app.config['GREYBOOK_ERROR_EMAIL_SUBJECT'], 34 | credentials=(app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD']), 35 | ) 36 | mail_handler.setLevel(logging.ERROR) 37 | mail_handler.setFormatter(request_formatter) 38 | 39 | if not app.debug: 40 | app.logger.addHandler(mail_handler) 41 | app.logger.addHandler(file_handler) 42 | app.logger.setLevel(logging.INFO) 43 | -------------------------------------------------------------------------------- /greybook/core/request.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy.record_queries import get_recorded_queries 2 | 3 | 4 | def register_request_handlers(app): 5 | @app.after_request 6 | def query_profiler(response): 7 | for q in get_recorded_queries(): 8 | if q.duration >= app.config['GREYBOOK_SLOW_QUERY_THRESHOLD']: 9 | app.logger.warning( 10 | 'Slow query: Duration: ' f'{q.duration:f}s\n Context: {q.context}\nQuery: {q.statement}\n' 11 | ) 12 | return response 13 | -------------------------------------------------------------------------------- /greybook/core/templating.py: -------------------------------------------------------------------------------- 1 | from flask_login import current_user 2 | from sqlalchemy import func, select 3 | 4 | from greybook.core.extensions import db 5 | from greybook.models import Admin, Category, Comment, Link 6 | 7 | 8 | def register_template_handlers(app): 9 | @app.context_processor 10 | def make_template_context(): 11 | admin = db.session.scalar(select(Admin)) 12 | categories = db.session.scalars(select(Category).order_by(Category.name)).all() 13 | links = db.session.scalars(select(Link).order_by(Link.name)).all() 14 | 15 | if current_user.is_authenticated: 16 | stmt = select(func.count(Comment.id)).filter_by(reviewed=False) 17 | unread_comments = db.session.scalars(stmt).one() 18 | else: 19 | unread_comments = None 20 | return dict(admin=admin, categories=categories, links=links, unread_comments=unread_comments) 21 | -------------------------------------------------------------------------------- /greybook/emails.py: -------------------------------------------------------------------------------- 1 | from threading import Thread 2 | 3 | from flask import current_app, url_for 4 | from flask_mailman import EmailMessage 5 | 6 | 7 | def _send_async_mail(app, message): 8 | with app.app_context(): 9 | message.send() 10 | 11 | 12 | def send_mail(subject, body, to): 13 | if current_app.debug: 14 | current_app.logger.debug('Skip sending email in debug mode.') 15 | current_app.logger.debug(f'To: {to}') 16 | current_app.logger.debug(f'Subject: {subject}') 17 | current_app.logger.debug(f'Body: {body}') 18 | return 19 | app = current_app._get_current_object() 20 | message = EmailMessage(subject, body=body, to=[to]) 21 | message.content_subtype = 'html' 22 | thr = Thread(target=_send_async_mail, args=[app, message]) 23 | thr.start() 24 | return thr 25 | 26 | 27 | def send_new_comment_email(post): 28 | post_url = url_for('blog.show_post', post_id=post.id, _external=True) + '#comments' 29 | send_mail( 30 | subject='New comment', 31 | body=f'

New comment in post {post.title}, click the link below to check:

' 32 | f'

{post_url}

' 33 | '

Do not reply this email.

', 34 | to=current_app.config['GREYBOOK_ADMIN_EMAIL'], 35 | ) 36 | 37 | 38 | def send_new_reply_email(comment): 39 | post_url = url_for('blog.show_post', post_id=comment.post_id, _external=True) + '#comments' 40 | send_mail( 41 | subject='New reply', 42 | body=f'

New reply for the comment you left in post {comment.post.title}, ' 43 | f'click the link below to check:

{post_url}

' 44 | '

Do not reply this email.

', 45 | to=comment.email, 46 | ) 47 | -------------------------------------------------------------------------------- /greybook/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from pathlib import Path 4 | 5 | BASE_DIR = Path(__file__).resolve().parent.parent 6 | SQLITE_PREFIX = 'sqlite:///' if sys.platform.startswith('win') else 'sqlite:////' 7 | 8 | 9 | class BaseConfig: 10 | SECRET_KEY = os.getenv('SECRET_KEY', 'dev key') 11 | 12 | DEBUG_TB_INTERCEPT_REDIRECTS = False 13 | DEBUG_TB_ENABLED = False 14 | 15 | SQLALCHEMY_RECORD_QUERIES = True 16 | 17 | CKEDITOR_ENABLE_CSRF = True 18 | CKEDITOR_FILE_UPLOADER = 'admin.upload_image' 19 | 20 | MAIL_SERVER = os.getenv('MAIL_SERVER') 21 | MAIL_PORT = 587 22 | MAIL_USE_TLS = True 23 | MAIL_USERNAME = os.getenv('MAIL_USERNAME') 24 | MAIL_PASSWORD = os.getenv('MAIL_PASSWORD') 25 | MAIL_DEFAULT_SENDER = f'Greybook <{MAIL_USERNAME}>' 26 | 27 | GREYBOOK_ADMIN_EMAIL = os.getenv('GREYBOOK_ADMIN_EMAIL', 'admin@helloflask.com') 28 | GREYBOOK_POST_PER_PAGE = 10 29 | GREYBOOK_MANAGE_POST_PER_PAGE = 15 30 | GREYBOOK_COMMENT_PER_PAGE = 15 31 | # ('theme name', 'display name') 32 | GREYBOOK_THEMES = {'default': 'Default', 'bluelog': 'Bluelog'} 33 | GREYBOOK_SLOW_QUERY_THRESHOLD = 1 34 | 35 | GREYBOOK_UPLOAD_PATH = os.getenv('GREYBOOK_UPLOAD_PATH', BASE_DIR / 'uploads') 36 | GREYBOOK_ALLOWED_IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif'] 37 | GREYBOOK_LOGGING_PATH = os.getenv('GREYBOOK_LOGGING_PATH', BASE_DIR / 'logs/greybook.log') 38 | GREYBOOK_ERROR_EMAIL_SUBJECT = '[Greybook] Application Error' 39 | 40 | 41 | class DevelopmentConfig(BaseConfig): 42 | SQLALCHEMY_DATABASE_URI = SQLITE_PREFIX + str(BASE_DIR / 'data-dev.db') 43 | 44 | 45 | class TestingConfig(BaseConfig): 46 | TESTING = True 47 | WTF_CSRF_ENABLED = False 48 | SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:' # in-memory database 49 | 50 | 51 | class ProductionConfig(BaseConfig): 52 | SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL', SQLITE_PREFIX + str(BASE_DIR / 'data.db')) 53 | 54 | 55 | config = {'development': DevelopmentConfig, 'testing': TestingConfig, 'production': ProductionConfig} 56 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/README.md: -------------------------------------------------------------------------------- 1 | CKEditor 4 2 | ========== 3 | 4 | Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 5 | http://ckeditor.com - See LICENSE.md for license information. 6 | 7 | CKEditor is a text editor to be used inside web pages. It's not a replacement 8 | for desktop text editors like Word or OpenOffice, but a component to be used as 9 | part of web applications and websites. 10 | 11 | ## Documentation 12 | 13 | The full editor documentation is available online at the following address: 14 | http://docs.ckeditor.com 15 | 16 | ## Installation 17 | 18 | Installing CKEditor is an easy task. Just follow these simple steps: 19 | 20 | 1. **Download** the latest version from the CKEditor website: 21 | http://ckeditor.com. You should have already completed this step, but be 22 | sure you have the very latest version. 23 | 2. **Extract** (decompress) the downloaded file into the root of your website. 24 | 25 | **Note:** CKEditor is by default installed in the `ckeditor` folder. You can 26 | place the files in whichever you want though. 27 | 28 | ## Checking Your Installation 29 | 30 | The editor comes with a few sample pages that can be used to verify that 31 | installation proceeded properly. Take a look at the `samples` directory. 32 | 33 | To test your installation, just call the following page at your website: 34 | 35 | http:////samples/index.html 36 | 37 | For example: 38 | 39 | http://www.example.com/ckeditor/samples/index.html 40 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | 6 | CKEDITOR.editorConfig = function( config ) { 7 | // Define changes to default configuration here. 8 | // For complete reference see: 9 | // http://docs.ckeditor.com/#!/api/CKEDITOR.config 10 | 11 | // The toolbar groups arrangement, optimized for two toolbar rows. 12 | config.toolbarGroups = [ 13 | { name: 'clipboard', groups: [ 'clipboard', 'undo' ] }, 14 | { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] }, 15 | { name: 'links' }, 16 | { name: 'insert' }, 17 | { name: 'forms' }, 18 | { name: 'tools' }, 19 | { name: 'document', groups: [ 'mode', 'document', 'doctools' ] }, 20 | { name: 'others' }, 21 | '/', 22 | { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }, 23 | { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] }, 24 | { name: 'styles' }, 25 | { name: 'colors' }, 26 | { name: 'about' } 27 | ]; 28 | 29 | // Remove some buttons provided by the standard plugins, which are 30 | // not needed in the Standard(s) toolbar. 31 | config.removeButtons = 'Underline,Subscript,Superscript'; 32 | 33 | // Set the most common block elements. 34 | config.format_tags = 'p;h1;h2;h3;pre'; 35 | 36 | // Simplify the dialog windows. 37 | config.removeDialogTabs = 'image:advanced;link:advanced'; 38 | }; 39 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/a11yhelp/dialogs/a11yhelp.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.dialog.add("a11yHelp",function(e){var a=e.lang.a11yhelp,b=e.lang.common.keyboard,q=CKEDITOR.tools.getNextId(),d={8:b[8],9:a.tab,13:b[13],16:b[16],17:b[17],18:b[18],19:a.pause,20:a.capslock,27:a.escape,33:a.pageUp,34:a.pageDown,35:b[35],36:b[36],37:a.leftArrow,38:a.upArrow,39:a.rightArrow,40:a.downArrow,45:a.insert,46:b[46],91:a.leftWindowKey,92:a.rightWindowKey,93:a.selectKey,96:a.numpad0,97:a.numpad1,98:a.numpad2,99:a.numpad3,100:a.numpad4,101:a.numpad5,102:a.numpad6,103:a.numpad7,104:a.numpad8, 6 | 105:a.numpad9,106:a.multiply,107:a.add,109:a.subtract,110:a.decimalPoint,111:a.divide,112:a.f1,113:a.f2,114:a.f3,115:a.f4,116:a.f5,117:a.f6,118:a.f7,119:a.f8,120:a.f9,121:a.f10,122:a.f11,123:a.f12,144:a.numLock,145:a.scrollLock,186:a.semiColon,187:a.equalSign,188:a.comma,189:a.dash,190:a.period,191:a.forwardSlash,192:a.graveAccent,219:a.openBracket,220:a.backSlash,221:a.closeBracket,222:a.singleQuote};d[CKEDITOR.ALT]=b[18];d[CKEDITOR.SHIFT]=b[16];d[CKEDITOR.CTRL]=CKEDITOR.env.mac?b[224]:b[17];var k= 7 | [CKEDITOR.ALT,CKEDITOR.SHIFT,CKEDITOR.CTRL],r=/\$\{(.*?)\}/g,t=function(a,b){var c=e.getCommandKeystroke(b);if(c){for(var l,f,h=[],g=0;g=l&&(c-=f,h.push(d[f]));h.push(d[c]||String.fromCharCode(c));c=h.join("+")}else c=a;return c};return{title:a.title,minWidth:600,minHeight:400,contents:[{id:"info",label:e.lang.common.generalTab,expand:!0,elements:[{type:"html",id:"legends",style:"white-space:normal;",focus:function(){this.getElement().focus()},html:function(){for(var b= 8 | '\x3cdiv class\x3d"cke_accessibility_legend" role\x3d"document" aria-labelledby\x3d"'+q+'_arialbl" tabIndex\x3d"-1"\x3e%1\x3c/div\x3e\x3cspan id\x3d"'+q+'_arialbl" class\x3d"cke_voice_label"\x3e'+a.contents+" \x3c/span\x3e",d=[],c=a.legend,l=c.length,f=0;f total ) { 35 | loaded = total; 36 | } 37 | 38 | if ( loaded > step * 4 && xhr.responseText.indexOf( 'incorrectFile' ) > 0 ) { 39 | xhr.aborted = true; 40 | xhr.onerror(); 41 | } else if ( loaded < total ) { 42 | evt.loaded = loaded; 43 | baseOnProgress( { loaded: loaded } ); 44 | progress(); 45 | } else { 46 | baseOnLoad( evt ); 47 | } 48 | }, 300 ); 49 | } 50 | 51 | progress(); 52 | }; 53 | 54 | this.abort = function() { 55 | this.aborted = true; 56 | this.onabort(); 57 | }; 58 | 59 | this.baseSend( data ); 60 | }; 61 | } )(); 62 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/az.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'az', { 6 | loadError: 'Faylını oxumaq mümkün deyil', 7 | networkError: 'Xəta baş verdi.', 8 | httpError404: 'Serverə göndərilməsinin zamanı xəta baş verdi (404 - fayl tapılmayıb)', 9 | httpError403: 'Serverə göndərilməsinin zamanı xəta baş verdi (403 - gadağandır)', 10 | httpError: 'Serverə göndərilməsinin zamanı xəta baş verdi (xətanın ststusu: %1)', 11 | noUrlError: 'Yükləmə linki təyin edilməyib', 12 | responseError: 'Serverin cavabı yanlışdır' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/ca.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'ca', { 6 | loadError: 'S\'ha produït un error durant la lectura del fitxer.', 7 | networkError: 'S\'ha produït un error de xarxa durant la càrrega del fitxer.', 8 | httpError404: 'S\'ha produït un error HTTP durant la càrrega del fitxer (404: Fitxer no trobat).', 9 | httpError403: 'S\'ha produït un error HTTP durant la càrrega del fitxer (403: Permís denegat).', 10 | httpError: 'S\'ha produït un error HTTP durant la càrrega del fitxer (estat d\'error: %1).', 11 | noUrlError: 'La URL de càrrega no està definida.', 12 | responseError: 'Resposta incorrecte del servidor' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/cs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'cs', { 6 | loadError: 'Při čtení souboru došlo k chybě.', 7 | networkError: 'Při nahrávání souboru došlo k chybě v síti.', 8 | httpError404: 'Při nahrávání souboru došlo k chybě HTTP (404: Soubor nenalezen).', 9 | httpError403: 'Při nahrávání souboru došlo k chybě HTTP (403: Zakázáno).', 10 | httpError: 'Při nahrávání souboru došlo k chybě HTTP (chybový stav: %1).', 11 | noUrlError: 'URL pro nahrání není zadána.', 12 | responseError: 'Nesprávná odpověď serveru.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/da.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'da', { 6 | loadError: 'Der skete en fejl ved indlæsningen af filen.', 7 | networkError: 'Der skete en netværks fejl under uploadingen.', 8 | httpError404: 'Der skete en HTTP fejl under uploadingen (404: File not found).', 9 | httpError403: 'Der skete en HTTP fejl under uploadingen (403: Forbidden).', 10 | httpError: 'Der skete en HTTP fejl under uploadingen (error status: %1).', 11 | noUrlError: 'Upload URL er ikke defineret.', 12 | responseError: 'Ikke korrekt server svar.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/de-ch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'de-ch', { 6 | loadError: 'Während dem Lesen der Datei ist ein Fehler aufgetreten.', 7 | networkError: 'Während dem Hochladen der Datei ist ein Netzwerkfehler aufgetreten.', 8 | httpError404: 'Während dem Hochladen der Datei ist ein HTTP-Fehler aufgetreten (404: Datei nicht gefunden).', 9 | httpError403: 'Während dem Hochladen der Datei ist ein HTTP-Fehler aufgetreten (403: Verboten).', 10 | httpError: 'Während dem Hochladen der Datei ist ein HTTP-Fehler aufgetreten (Fehlerstatus: %1).', 11 | noUrlError: 'Hochlade-URL ist nicht definiert.', 12 | responseError: 'Falsche Antwort des Servers.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/de.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'de', { 6 | loadError: 'Während des Lesens der Datei ist ein Fehler aufgetreten.', 7 | networkError: 'Während des Hochladens der Datei ist ein Netzwerkfehler aufgetreten.', 8 | httpError404: 'Während des Hochladens der Datei ist ein HTTP-Fehler aufgetreten (404: Datei nicht gefunden).', 9 | httpError403: 'Während des Hochladens der Datei ist ein HTTP-Fehler aufgetreten (403: Verboten).', 10 | httpError: 'Während des Hochladens der Datei ist ein HTTP-Fehler aufgetreten (Fehlerstatus: %1).', 11 | noUrlError: 'Hochlade-URL ist nicht definiert.', 12 | responseError: 'Falsche Antwort des Servers.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/en-au.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'en-au', { 6 | loadError: 'Error occurred during file read.', 7 | networkError: 'Network error occurred during file upload.', 8 | httpError404: 'HTTP error occurred during file upload (404: File not found).', 9 | httpError403: 'HTTP error occurred during file upload (403: Forbidden).', 10 | httpError: 'HTTP error occurred during file upload (error status: %1).', 11 | noUrlError: 'Upload URL is not defined.', 12 | responseError: 'Incorrect server response.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/en.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'en', { 6 | loadError: 'Error occurred during file read.', 7 | networkError: 'Network error occurred during file upload.', 8 | httpError404: 'HTTP error occurred during file upload (404: File not found).', 9 | httpError403: 'HTTP error occurred during file upload (403: Forbidden).', 10 | httpError: 'HTTP error occurred during file upload (error status: %1).', 11 | noUrlError: 'Upload URL is not defined.', 12 | responseError: 'Incorrect server response.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/eo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'eo', { 6 | loadError: 'Eraro okazis dum la dosiera legado.', 7 | networkError: 'Reta eraro okazis dum la dosiera alŝuto.', 8 | httpError404: 'HTTP eraro okazis dum la dosiera alŝuto (404: dosiero ne trovita).', 9 | httpError403: 'HTTP eraro okazis dum la dosiera alŝuto (403: malpermesita).', 10 | httpError: 'HTTP eraro okazis dum la dosiera alŝuto (erara stato: %1).', 11 | noUrlError: 'Alŝuta URL ne estas difinita.', 12 | responseError: 'Malĝusta respondo de la servilo.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/es-mx.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'es-mx', { 6 | loadError: 'Ha ocurrido un error al leer el archivo', 7 | networkError: 'Ha ocurrido un error de red durante la carga del archivo.', 8 | httpError404: 'Se ha producido un error HTTP durante la subida de archivos (404: archivo no encontrado).', 9 | httpError403: 'Se ha producido un error HTTP durante la subida de archivos (403: Prohibido).', 10 | httpError: 'Se ha producido un error HTTP durante la subida de archivos (error: %1).', 11 | noUrlError: 'La URL de subida no está definida.', 12 | responseError: 'Respuesta incorrecta del servidor.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/es.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'es', { 6 | loadError: 'Ha ocurrido un error durante la lectura del archivo.', 7 | networkError: 'Error de red ocurrido durante carga de archivo.', 8 | httpError404: 'Un error HTTP ha ocurrido durante la carga del archivo (404: Archivo no encontrado).', 9 | httpError403: 'Un error HTTP ha ocurrido durante la carga del archivo (403: Prohibido).', 10 | httpError: 'Error HTTP ocurrido durante la carga del archivo (Estado del error: %1).', 11 | noUrlError: 'URL cargada no está definida.', 12 | responseError: 'Respueta del servidor incorrecta.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/eu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'eu', { 6 | loadError: 'Errorea gertatu da fitxategia irakurtzean.', 7 | networkError: 'Sareko errorea gertatu da fitxategia kargatzean.', 8 | httpError404: 'HTTP errorea gertatu da fitxategia kargatzean (404: Fitxategia ez da aurkitu).', 9 | httpError403: 'HTTP errorea gertatu da fitxategia kargatzean (403: Debekatuta).', 10 | httpError: 'HTTP errorea gertatu da fitxategia kargatzean (errore-egoera: %1).', 11 | noUrlError: 'Kargatzeko URLa definitu gabe.', 12 | responseError: 'Zerbitzariaren erantzun okerra.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/fr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'fr', { 6 | loadError: 'Une erreur est survenue lors de la lecture du fichier.', 7 | networkError: 'Une erreur réseau est survenue lors du téléversement du fichier.', 8 | httpError404: 'Une erreur HTTP est survenue durant le téléversement du fichier (404 : fichier non trouvé).', 9 | httpError403: 'Une erreur HTTP est survenue durant le téléversement du fichier (403 : accès refusé).', 10 | httpError: 'Une erreur HTTP est survenue durant le téléversement du fichier (erreur : %1).', 11 | noUrlError: 'L\'URL de téléversement n\'est pas spécifiée.', 12 | responseError: 'Réponse du serveur incorrecte.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/gl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'gl', { 6 | loadError: 'Produciuse un erro durante a lectura do ficheiro.', 7 | networkError: 'Produciuse un erro na rede durante o envío do ficheiro.', 8 | httpError404: 'Produciuse un erro HTTP durante o envío do ficheiro (404: Ficheiro non atopado).', 9 | httpError403: 'Produciuse un erro HTTP durante o envío do ficheiro (403: Acceso denegado).', 10 | httpError: 'Produciuse un erro HTTP durante o envío do ficheiro (erro de estado: %1).', 11 | noUrlError: 'Non foi definido o URL para o envío.', 12 | responseError: 'Resposta incorrecta do servidor.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/hr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'hr', { 6 | loadError: 'Greška prilikom čitanja datoteke.', 7 | networkError: 'Mrežna greška prilikom slanja datoteke.', 8 | httpError404: 'HTTP greška tijekom slanja datoteke (404: datoteka nije pronađena).', 9 | httpError403: 'HTTP greška tijekom slanja datoteke (403: Zabranjeno).', 10 | httpError: 'HTTP greška tijekom slanja datoteke (greška status: %1).', 11 | noUrlError: 'URL za slanje nije podešen.', 12 | responseError: 'Neispravni odgovor servera.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/hu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'hu', { 6 | loadError: 'Hiba történt a fájl olvasása közben.', 7 | networkError: 'Hálózati hiba történt a fájl feltöltése közben.', 8 | httpError404: 'HTTP hiba történt a fájl feltöltése alatt (404: A fájl nem található).', 9 | httpError403: 'HTTP hiba történt a fájl feltöltése alatt (403: Tiltott).', 10 | httpError: 'HTTP hiba történt a fájl feltöltése alatt (hiba státusz: %1).', 11 | noUrlError: 'Feltöltési URL nincs megadva.', 12 | responseError: 'Helytelen szerver válasz.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/id.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'id', { 6 | loadError: 'Error terjadi ketika berkas dibaca', 7 | networkError: 'Jaringan error terjadi ketika mengunggah berkas', 8 | httpError404: 'HTTP error terjadi ketika mengunggah berkas (404: Berkas tidak ditemukan)', 9 | httpError403: 'HTTP error terjadi ketika mengunggah berkas (403: Gangguan)', 10 | httpError: 'HTTP error terjadi ketika mengunggah berkas (status error: %1)', 11 | noUrlError: 'Unggahan URL tidak terdefinisi', 12 | responseError: 'Respon server tidak sesuai' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/it.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'it', { 6 | loadError: 'Si è verificato un errore durante la lettura del file.', 7 | networkError: 'Si è verificato un errore di rete durante il caricamento del file.', 8 | httpError404: 'Si è verificato un errore HTTP durante il caricamento del file (404: file non trovato).', 9 | httpError403: 'Si è verificato un errore HTTP durante il caricamento del file (403: accesso negato).', 10 | httpError: 'Si è verificato un errore HTTP durante il caricamento del file (stato dell\'errore: %1).', 11 | noUrlError: 'L\'URL per il caricamento non è stato definito.', 12 | responseError: 'La risposta del server non è corretta.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/ja.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'ja', { 6 | loadError: 'ファイルの読み込み中にエラーが発生しました。', 7 | networkError: 'ファイルのアップロード中にネットワークエラーが発生しました。', 8 | httpError404: 'ファイルのアップロード中にHTTPエラーが発生しました。(404: File not found)', 9 | httpError403: 'ファイルのアップロード中にHTTPエラーが発生しました。(403: Forbidden)', 10 | httpError: 'ファイルのアップロード中にHTTPエラーが発生しました。(error status: %1)', 11 | noUrlError: 'アップロードURLが定義されていません。', 12 | responseError: 'サーバーの応答が不正です。' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/km.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'km', { 6 | loadError: 'មាន​បញ្ហា​កើតឡើង​ក្នុង​ពេល​អាន​ឯកសារ។', 7 | networkError: 'មាន​បញ្ហា​បណ្ដាញ​កើត​ឡើង​ក្នុង​ពេល​ផ្ទុកឡើង​ឯកសារ។', 8 | httpError404: 'មាន​បញ្ហា HTTP កើត​ឡើង​ក្នុង​ពេល​ផ្ទុកឡើង​ឯកសារ (404៖ រក​ឯកសារ​មិន​ឃើញ)។', 9 | httpError403: 'មាន​បញ្ហា HTTP កើត​ឡើង​ក្នុង​ពេល​ផ្ទុកឡើង​ឯកសារ (403៖ ហាមឃាត់)។', 10 | httpError: 'មាន​បញ្ហា HTTP កើត​ឡើង​ក្នុង​ពេល​ផ្ទុកឡើង​ឯកសារ (ស្ថានភាព​កំហុស៖ %1)។', 11 | noUrlError: 'មិន​មាន​បញ្ជាក់ URL ផ្ទុក​ឡើង។', 12 | responseError: 'ការ​ឆ្លើយតប​របស់​ម៉ាស៊ីនបម្រើ មិន​ត្រឹមត្រូវ។' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/ko.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'ko', { 6 | loadError: '파일을 읽는 중 오류가 발생했습니다.', 7 | networkError: '파일 업로드 중 네트워크 오류가 발생했습니다.', 8 | httpError404: '파일 업로드중 HTTP 오류가 발생했습니다 (404: 파일 찾을수 없음).', 9 | httpError403: '파일 업로드중 HTTP 오류가 발생했습니다 (403: 권한 없음).', 10 | httpError: '파일 업로드중 HTTP 오류가 발생했습니다 (오류 코드 %1).', 11 | noUrlError: '업로드 주소가 정의되어 있지 않습니다.', 12 | responseError: '잘못된 서버 응답.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/ku.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'ku', { 6 | loadError: 'هەڵەیەک ڕوویدا لە ماوەی خوێندنەوەی پەڕگەکە.', 7 | networkError: 'هەڵەیەکی ڕایەڵە ڕوویدا لە ماوەی بارکردنی پەڕگەکە.', 8 | httpError404: 'هەڵەیەک ڕوویدا لە ماوەی بارکردنی پەڕگەکە (404: پەڕگەکە نەدۆزراوە).', 9 | httpError403: 'هەڵەیەک ڕوویدا لە ماوەی بارکردنی پەڕگەکە (403: قەدەغەکراو).', 10 | httpError: 'هەڵەیەک ڕوویدا لە ماوەی بارکردنی پەڕگەکە (دۆخی هەڵە: %1).', 11 | noUrlError: 'بەستەری پەڕگەکە پێناسە نەکراوە.', 12 | responseError: 'وەڵامێکی نادروستی سێرڤەر.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/nb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'nb', { 6 | loadError: 'Feil oppsto under filinnlesing.', 7 | networkError: 'Nettverksfeil oppsto under filopplasting.', 8 | httpError404: 'HTTP-feil oppsto under filopplasting (404: Fant ikke filen).', 9 | httpError403: 'HTTP-feil oppsto under filopplasting (403: Ikke tillatt).', 10 | httpError: 'HTTP-feil oppsto under filopplasting (feilstatus: %1).', 11 | noUrlError: 'URL for opplasting er ikke oppgitt.', 12 | responseError: 'Ukorrekt svar fra serveren.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/nl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'nl', { 6 | loadError: 'Fout tijdens lezen van bestand.', 7 | networkError: 'Netwerkfout tijdens uploaden van bestand.', 8 | httpError404: 'HTTP fout tijdens uploaden van bestand (404: Bestand niet gevonden).', 9 | httpError403: 'HTTP fout tijdens uploaden van bestand (403: Verboden).', 10 | httpError: 'HTTP fout tijdens uploaden van bestand (fout status: %1).', 11 | noUrlError: 'Upload URL is niet gedefinieerd.', 12 | responseError: 'Ongeldig antwoord van server.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/oc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'oc', { 6 | loadError: 'Una error s\'es produita pendent la lectura del fichièr.', 7 | networkError: 'Una error de ret s\'es produita pendent lo mandadís del fichièr.', 8 | httpError404: 'Una error HTTP s\'es produita pendent lo mandadís del fichièr (404 : fichièr pas trobat).', 9 | httpError403: 'Una error HTTP s\'es produita pendent lo mandadís del fichièr (403 : accès refusat).', 10 | httpError: 'Una error HTTP s\'es produita pendent lo mandadís del fichièr (error : %1).', 11 | noUrlError: 'L\'URL de mandadís es pas especificada.', 12 | responseError: 'Responsa del servidor incorrècta.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/pl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'pl', { 6 | loadError: 'Błąd podczas odczytu pliku.', 7 | networkError: 'W trakcie wysyłania pliku pojawił się błąd sieciowy.', 8 | httpError404: 'Błąd HTTP w trakcie wysyłania pliku (404: Nie znaleziono pliku).', 9 | httpError403: 'Błąd HTTP w trakcie wysyłania pliku (403: Zabroniony).', 10 | httpError: 'Błąd HTTP w trakcie wysyłania pliku (status błędu: %1).', 11 | noUrlError: 'Nie zdefiniowano adresu URL do przesłania pliku.', 12 | responseError: 'Niepoprawna odpowiedź serwera.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/pt-br.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'pt-br', { 6 | loadError: 'Um erro ocorreu durante a leitura do arquivo.', 7 | networkError: 'Um erro de rede ocorreu durante o envio do arquivo.', 8 | httpError404: 'Um erro HTTP ocorreu durante o envio do arquivo (404: Arquivo não encontrado).', 9 | httpError403: 'Um erro HTTP ocorreu durante o envio do arquivo (403: Proibido).', 10 | httpError: 'Um erro HTTP ocorreu durante o envio do arquivo (status do erro: %1)', 11 | noUrlError: 'A URL de upload não está definida.', 12 | responseError: 'Resposta incorreta do servidor.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/pt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'pt', { 6 | loadError: 'Ocorreu um erro ao ler o ficheiro', 7 | networkError: 'Ocorreu um erro de rede ao carregar o ficheiro.', 8 | httpError404: 'HTTP error occurred during file upload (404: File not found).', // MISSING 9 | httpError403: 'HTTP error occurred during file upload (403: Forbidden).', // MISSING 10 | httpError: 'HTTP error occurred during file upload (error status: %1).', // MISSING 11 | noUrlError: 'Upload URL is not defined.', // MISSING 12 | responseError: 'Incorrect server response.' // MISSING 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/ro.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'ro', { 6 | loadError: 'Eroare în timpul citirii fișierului.', 7 | networkError: 'Eroare de rețea în timpul încărcării fișierului.', 8 | httpError404: 'Eroare HTTP în timpul încărcării fișierului (404: Fișier negăsit).', 9 | httpError403: 'Eroare HTTP în timpul încărcării fișierului (403: Operașie nepermisă).', 10 | httpError: 'Eroare HTTP în timpul încărcării fișierului (stare eroiare: %1).', 11 | noUrlError: 'URL-ul de ăncărcare nu este specificat.', 12 | responseError: 'Răspuns server incorect.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/ru.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'ru', { 6 | loadError: 'Ошибка при чтении файла', 7 | networkError: 'Сетевая ошибка при загрузке файла', 8 | httpError404: 'HTTP ошибка при загрузке файла (404: Файл не найден)', 9 | httpError403: 'HTTP ошибка при загрузке файла (403: Запрещено)', 10 | httpError: 'HTTP ошибка при загрузке файла (%1)', 11 | noUrlError: 'Не определен URL для загрузки файлов', 12 | responseError: 'Некорректный ответ сервера' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/sk.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'sk', { 6 | loadError: 'Počas čítania súboru nastala chyba.', 7 | networkError: 'Počas nahrávania súboru nastala chyba siete.', 8 | httpError404: 'Počas nahrávania súboru nastala HTTP chyba (404: Súbor nebol nájdený).', 9 | httpError403: 'Počas nahrávania súboru nastala HTTP chyba (403: Zakázaný).', 10 | httpError: 'Počas nahrávania súboru nastala HTTP chyba (error status: %1).', 11 | noUrlError: 'URL nahrávania nie je definovaný.', 12 | responseError: 'Nesprávna odpoveď servera.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/sq.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'sq', { 6 | loadError: 'Gabimi u paraqit gjatë leximit të skedës.', 7 | networkError: 'Gabimi në rrjetë u paraqitë gjatë ngarkimit të skedës.', 8 | httpError404: 'Gabimi në HTTP u paraqit gjatë ngarkimit të skedës (404: Skeda nuk u gjetë).', 9 | httpError403: 'Gabimi në HTTP u paraqitë gjatë ngarkimit të skedës (403: E ndaluar).', 10 | httpError: 'Gabimi në HTTP u paraqit gjatë ngarkimit të skedës (gjendja e gabimit: %1).', 11 | noUrlError: 'URL e ngarkimit nuk është vendosur.', 12 | responseError: 'Përgjigje e gabuar e serverit.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/sv.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'sv', { 6 | loadError: 'Fel uppstod vid filläsning', 7 | networkError: 'Nätverksfel uppstod vid filuppladdning.', 8 | httpError404: 'HTTP-fel uppstod vid filuppladdning (404: Fil hittades inte).', 9 | httpError403: 'HTTP-fel uppstod vid filuppladdning (403: Förbjuden).', 10 | httpError: 'HTTP-fel uppstod vid filuppladdning (felstatus: %1).', 11 | noUrlError: 'URL för uppladdning inte definierad.', 12 | responseError: 'Felaktigt serversvar.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/tr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'tr', { 6 | loadError: 'Dosya okunurken hata oluştu.', 7 | networkError: 'Dosya gönderilirken ağ hatası oluştu.', 8 | httpError404: 'Dosya gönderilirken HTTP hatası oluştu (404: Dosya bulunamadı).', 9 | httpError403: 'Dosya gönderilirken HTTP hatası oluştu (403: Yasaklı).', 10 | httpError: 'Dosya gönderilirken HTTP hatası oluştu (hata durumu: %1).', 11 | noUrlError: 'Gönderilecek URL belirtilmedi.', 12 | responseError: 'Sunucu cevap veremedi.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/ug.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'ug', { 6 | loadError: 'ھۆججەت ئوقۇشتا خاتالىق كۆرۈلدى', 7 | networkError: 'ھۆججەت يۈكلەشتە تور خاتالىقى كۆرۈلدى.', 8 | httpError404: 'ھۆججەت يۈكلىگەندە HTTP خاتالىقى كۆرۈلدى (404: ھۆججەت تېپىلمىدى).', 9 | httpError403: 'ھۆججەت يۈكلىگەندە HTTP خاتالىقى كۆرۈلدى (403: چەكلەنگەن).', 10 | httpError: 'ھۆججەت يۈكلىگەندە HTTP خاتالىقى كۆرۈلدى (404: خاتالىق نىسپىتى: 1%).', 11 | noUrlError: 'چىقىردىغان ئۇلانما تەڭشەلمىگەن .', 12 | responseError: 'مۇلازىمىتىردا ئىنكاس يوق .' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/uk.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'uk', { 6 | loadError: 'Виникла помилка під час читання файлу', 7 | networkError: 'Під час завантаження файлу виникла помилка мережі.', 8 | httpError404: 'Під час завантаження файлу виникла помилка HTTP (404: Файл не знайдено).', 9 | httpError403: 'Під час завантаження файлу виникла помилка HTTP (403: Доступ заборонено).', 10 | httpError: 'Під час завантаження файлу виникла помилка (статус помилки: %1).', 11 | noUrlError: 'URL завантаження не визначений.', 12 | responseError: 'Невірна відповідь сервера.' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'zh-cn', { 6 | loadError: '读取文件时发生错误', 7 | networkError: '上传文件时发生网络错误', 8 | httpError404: '上传文件时发生 HTTP 错误(404:无法找到文件)', 9 | httpError403: '上传文件时发生 HTTP 错误(403:禁止访问)', 10 | httpError: '上传文件时发生 HTTP 错误(错误代码:%1)', 11 | noUrlError: '上传的 URL 未定义', 12 | responseError: '不正确的服务器响应' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/filetools/lang/zh.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang( 'filetools', 'zh', { 6 | loadError: '在讀取檔案時發生錯誤。', 7 | networkError: '在上傳檔案時發生網路錯誤。', 8 | httpError404: '在上傳檔案時發生 HTTP 錯誤(404:檔案找不到)。', 9 | httpError403: '在上傳檔案時發生 HTTP 錯誤(403:禁止)。', 10 | httpError: '在上傳檔案時發生 HTTP 錯誤(錯誤狀態:%1)。', 11 | noUrlError: '上傳的 URL 未被定義。', 12 | responseError: '不正確的伺服器回應。' 13 | } ); 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/plugins/icons.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/icons_hidpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/plugins/icons_hidpi.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/image/images/noimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/plugins/image/images/noimage.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/link/dialogs/anchor.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.dialog.add("anchor",function(c){function e(b,a){return b.createFakeElement(b.document.createElement("a",{attributes:a}),"cke_anchor","anchor")}return{title:c.lang.link.anchor.title,minWidth:300,minHeight:60,onOk:function(){var b=CKEDITOR.tools.trim(this.getValueOf("info","txtName")),a={id:b,name:b,"data-cke-saved-name":b};this._.selectedElement?this._.selectedElement.data("cke-realelement")?(b=e(c,a),b.replace(this._.selectedElement),CKEDITOR.env.ie&&c.getSelection().selectElement(b)):this._.selectedElement.setAttributes(a): 6 | (b=(b=c.getSelection())&&b.getRanges()[0],b.collapsed?(a=e(c,a),b.insertNode(a)):(CKEDITOR.env.ie&&9>CKEDITOR.env.version&&(a["class"]="cke_anchor"),a=new CKEDITOR.style({element:"a",attributes:a}),a.type=CKEDITOR.STYLE_INLINE,a.applyToRange(b)))},onHide:function(){delete this._.selectedElement},onShow:function(){var b=c.getSelection(),a;a=b.getRanges()[0];var d=b.getSelectedElement();a.shrink(CKEDITOR.SHRINK_ELEMENT);a=(d=a.getEnclosedNode())&&d.type===CKEDITOR.NODE_ELEMENT&&("anchor"===d.data("cke-real-element-type")|| 7 | d.is("a"))?d:void 0;var f=(d=a&&a.data("cke-realelement"))?CKEDITOR.plugins.link.tryRestoreFakeAnchor(c,a):CKEDITOR.plugins.link.getSelectedLink(c);if(f){this._.selectedElement=f;var e=f.data("cke-saved-name");this.setValueOf("info","txtName",e||"");!d&&b.selectElement(f);a&&(this._.selectedElement=a)}this.getContentElement("info","txtName").focus()},contents:[{id:"info",label:c.lang.link.anchor.title,accessKey:"I",elements:[{type:"text",id:"txtName",label:c.lang.link.anchor.name,required:!0,validate:function(){return this.getValue()? 8 | !0:(alert(c.lang.link.anchor.errorName),!1)}}]}]}}); -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/link/images/anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/plugins/link/images/anchor.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/link/images/hidpi/anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/plugins/link/images/hidpi/anchor.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/magicline/images/hidpi/icon-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/plugins/magicline/images/hidpi/icon-rtl.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/magicline/images/hidpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/plugins/magicline/images/hidpi/icon.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/magicline/images/icon-rtl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/plugins/magicline/images/icon-rtl.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/magicline/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/plugins/magicline/images/icon.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/popup/plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | 6 | CKEDITOR.plugins.add( 'popup' ); 7 | 8 | CKEDITOR.tools.extend( CKEDITOR.editor.prototype, { 9 | /** 10 | * Opens Browser in a popup. The `width` and `height` parameters accept 11 | * numbers (pixels) or percent (of screen size) values. 12 | * 13 | * @member CKEDITOR.editor 14 | * @param {String} url The url of the external file browser. 15 | * @param {Number/String} [width='80%'] Popup window width. 16 | * @param {Number/String} [height='70%'] Popup window height. 17 | * @param {String} [options='location=no,menubar=no,toolbar=no,dependent=yes,minimizable=no,modal=yes,alwaysRaised=yes,resizable=yes,scrollbars=yes'] 18 | * Popup window features. 19 | */ 20 | popup: function( url, width, height, options ) { 21 | width = width || '80%'; 22 | height = height || '70%'; 23 | 24 | if ( typeof width == 'string' && width.length > 1 && width.substr( width.length - 1, 1 ) == '%' ) 25 | width = parseInt( window.screen.width * parseInt( width, 10 ) / 100, 10 ); 26 | 27 | if ( typeof height == 'string' && height.length > 1 && height.substr( height.length - 1, 1 ) == '%' ) 28 | height = parseInt( window.screen.height * parseInt( height, 10 ) / 100, 10 ); 29 | 30 | if ( width < 640 ) 31 | width = 640; 32 | 33 | if ( height < 420 ) 34 | height = 420; 35 | 36 | var top = parseInt( ( window.screen.height - height ) / 2, 10 ), 37 | left = parseInt( ( window.screen.width - width ) / 2, 10 ); 38 | 39 | options = ( options || 'location=no,menubar=no,toolbar=no,dependent=yes,minimizable=no,modal=yes,alwaysRaised=yes,resizable=yes,scrollbars=yes' ) + ',width=' + width + 40 | ',height=' + height + 41 | ',top=' + top + 42 | ',left=' + left; 43 | 44 | var popupWindow = window.open( '', null, options, true ); 45 | 46 | // Blocked by a popup blocker. 47 | if ( !popupWindow ) 48 | return false; 49 | 50 | try { 51 | // Chrome is problematic with moveTo/resizeTo, but it's not really needed here (https://dev.ckeditor.com/ticket/8855). 52 | var ua = navigator.userAgent.toLowerCase(); 53 | if ( ua.indexOf( ' chrome/' ) == -1 ) { 54 | popupWindow.moveTo( left, top ); 55 | popupWindow.resizeTo( width, height ); 56 | } 57 | popupWindow.focus(); 58 | popupWindow.location.href = url; 59 | } catch ( e ) { 60 | popupWindow = window.open( url, null, options, true ); 61 | } 62 | 63 | return true; 64 | } 65 | } ); 66 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/scayt/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | SCAYT plugin for CKEditor 4 Changelog 2 | ==================== 3 | ### CKEditor 4.5.6 4 | 5 | New Features: 6 | * CKEditor [language addon](http://ckeditor.com/addon/language) support 7 | * CKEditor [placeholder addon](http://ckeditor.com/addon/placeholder) support 8 | * Drag and Drop support 9 | * *Experimental* GRAYT functionality http://www.webspellchecker.net/samples/scayt-ckeditor-plugin.html#25 10 | 11 | Fixed issues: 12 | * [#98](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/98) SCAYT Affects Dialog Double Click. Fixed in SCAYT Core. 13 | * [#102](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/102) SCAYT Core performance enhancements 14 | * [#104](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/104) SCAYT's spans leak into the clipboard and after pasting 15 | * [#105](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/105) Javascript error fired in case of multiple instances of CKEditor in one page 16 | * [#107](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/107) SCAYT should not check non-editable parts of content 17 | * [#108](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/108) Latest SCAYT copies id of editor element to the iframe 18 | * SCAYT stops working when CKEditor Undo plug-in not enabled 19 | * Issue with pasting SCAYT markup in CKEditor 20 | * [#32](https://github.com/WebSpellChecker/ckeditor-plugin-wsc/issues/32) SCAYT stops working after pressing Cancel button in WSC dialog 21 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/scayt/LICENSE.md: -------------------------------------------------------------------------------- 1 | Software License Agreement 2 | ========================== 3 | 4 | **CKEditor SCAYT Plugin** 5 | Copyright © 2012, [CKSource](http://cksource.com) - Frederico Knabben. All rights reserved. 6 | 7 | Licensed under the terms of any of the following licenses at your choice: 8 | 9 | * GNU General Public License Version 2 or later (the "GPL"): 10 | http://www.gnu.org/licenses/gpl.html 11 | 12 | * GNU Lesser General Public License Version 2.1 or later (the "LGPL"): 13 | http://www.gnu.org/licenses/lgpl.html 14 | 15 | * Mozilla Public License Version 1.1 or later (the "MPL"): 16 | http://www.mozilla.org/MPL/MPL-1.1.html 17 | 18 | You are not required to, but if you want to explicitly declare the license you have chosen to be bound to when using, reproducing, modifying and distributing this software, just include a text file titled "legal.txt" in your version of this software, indicating your license choice. 19 | 20 | Sources of Intellectual Property Included in this plugin 21 | -------------------------------------------------------- 22 | 23 | Where not otherwise indicated, all plugin content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, the plugin will incorporate work done by developers outside of CKSource with their express permission. 24 | 25 | Trademarks 26 | ---------- 27 | 28 | CKEditor is a trademark of CKSource - Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders. 29 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/scayt/README.md: -------------------------------------------------------------------------------- 1 | CKEditor SCAYT Plugin 2 | ===================== 3 | 4 | This plugin brings Spell Check As You Type (SCAYT) into up to CKEditor 4+. 5 | 6 | SCAYT is a "installation-less", using the web-services of [WebSpellChecker.net](http://www.webspellchecker.net/). It's an out of the box solution. 7 | 8 | Installation 9 | ------------ 10 | 11 | 1. Clone/copy this repository contents in a new "plugins/scayt" folder in your CKEditor installation. 12 | 2. Enable the "scayt" plugin in the CKEditor configuration file (config.js): 13 | 14 | config.extraPlugins = 'scayt'; 15 | 16 | That's all. SCAYT will appear on the editor toolbar and will be ready to use. 17 | 18 | License 19 | ------- 20 | 21 | Licensed under the terms of any of the following licenses at your choice: [GPL](http://www.gnu.org/licenses/gpl.html), [LGPL](http://www.gnu.org/licenses/lgpl.html) and [MPL](http://www.mozilla.org/MPL/MPL-1.1.html). 22 | 23 | See LICENSE.md for more information. 24 | 25 | Developed in cooperation with [WebSpellChecker.net](http://www.webspellchecker.net/). 26 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/scayt/dialogs/dialog.css: -------------------------------------------------------------------------------- 1 | div.cke_dialog_ui_scaytItemList { 2 | border: 1px solid #c9cccf; 3 | } 4 | 5 | .cke_scaytItemList-child { 6 | position: relative; 7 | padding: 6px 30px 6px 5px; 8 | overflow: hidden; 9 | text-overflow: ellipsis; 10 | white-space: nowrap; 11 | } 12 | 13 | .cke_scaytItemList-child:hover { 14 | background: #ebebeb; 15 | } 16 | 17 | .cke_scaytItemList-child .cke_scaytItemList_remove { 18 | position: absolute; 19 | top: 0; 20 | right: 5px; 21 | width: 26px; 22 | height: 26px; 23 | } 24 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/scayt/dialogs/toolbar.css: -------------------------------------------------------------------------------- 1 | a 2 | { 3 | text-decoration:none; 4 | padding: 2px 4px 4px 6px; 5 | display : block; 6 | border-width: 1px; 7 | border-style: solid; 8 | margin : 0px; 9 | } 10 | 11 | a.cke_scayt_toogle:hover, 12 | a.cke_scayt_toogle:focus, 13 | a.cke_scayt_toogle:active 14 | { 15 | border-color: #316ac5; 16 | background-color: #dff1ff; 17 | color : #000; 18 | cursor: pointer; 19 | margin : 0px; 20 | } 21 | a.cke_scayt_toogle { 22 | color : #316ac5; 23 | border-color: #fff; 24 | } 25 | .scayt_enabled a.cke_scayt_item { 26 | color : #316ac5; 27 | border-color: #fff; 28 | margin : 0px; 29 | } 30 | .scayt_disabled a.cke_scayt_item { 31 | color : gray; 32 | border-color : #fff; 33 | } 34 | .scayt_enabled a.cke_scayt_item:hover, 35 | .scayt_enabled a.cke_scayt_item:focus, 36 | .scayt_enabled a.cke_scayt_item:active 37 | { 38 | border-color: #316ac5; 39 | background-color: #dff1ff; 40 | color : #000; 41 | cursor: pointer; 42 | } 43 | .scayt_disabled a.cke_scayt_item:hover, 44 | .scayt_disabled a.cke_scayt_item:focus, 45 | .scayt_disabled a.cke_scayt_item:active 46 | { 47 | border-color: gray; 48 | background-color: #dff1ff; 49 | color : gray; 50 | cursor: no-drop; 51 | } 52 | .cke_scayt_set_on, .cke_scayt_set_off 53 | { 54 | display: none; 55 | } 56 | .scayt_enabled .cke_scayt_set_on 57 | { 58 | display: none; 59 | } 60 | .scayt_disabled .cke_scayt_set_on 61 | { 62 | display: inline; 63 | } 64 | .scayt_disabled .cke_scayt_set_off 65 | { 66 | display: none; 67 | } 68 | .scayt_enabled .cke_scayt_set_off 69 | { 70 | display: inline; 71 | } 72 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/scayt/skins/moono-lisa/scayt.css: -------------------------------------------------------------------------------- 1 | .scayt-lang-list > div 2 | { 3 | padding-bottom: 6px !important; 4 | } 5 | 6 | .scayt-lang-list > div input 7 | { 8 | margin-right: 4px; 9 | } 10 | 11 | #scayt_about_ 12 | { 13 | margin: 30px auto 0 auto; 14 | } 15 | 16 | #scayt_about_ p 17 | { 18 | text-align: center; 19 | margin-bottom: 10px; 20 | } 21 | 22 | .cke_dialog_contents_body div[name=dictionaries] .cke_dialog_ui_hbox_last > a.cke_dialog_ui_button 23 | { 24 | margin-top: 0; 25 | } 26 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/specialchar/dialogs/lang/_translationstatus.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 2 | For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 3 | 4 | cs.js Found: 118 Missing: 0 5 | cy.js Found: 118 Missing: 0 6 | de.js Found: 118 Missing: 0 7 | el.js Found: 16 Missing: 102 8 | eo.js Found: 118 Missing: 0 9 | et.js Found: 31 Missing: 87 10 | fa.js Found: 24 Missing: 94 11 | fi.js Found: 23 Missing: 95 12 | fr.js Found: 118 Missing: 0 13 | hr.js Found: 23 Missing: 95 14 | it.js Found: 118 Missing: 0 15 | nb.js Found: 118 Missing: 0 16 | nl.js Found: 118 Missing: 0 17 | no.js Found: 118 Missing: 0 18 | tr.js Found: 118 Missing: 0 19 | ug.js Found: 39 Missing: 79 20 | zh-cn.js Found: 118 Missing: 0 21 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/specialchar/dialogs/lang/ja.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang("specialchar","ja",{euro:"ユーロ記号",lsquo:"左シングル引用符",rsquo:"右シングル引用符",ldquo:"左ダブル引用符",rdquo:"右ダブル引用符",ndash:"半角ダッシュ",mdash:"全角ダッシュ",iexcl:"逆さ感嘆符",cent:"セント記号",pound:"ポンド記号",curren:"通貨記号",yen:"円記号",brvbar:"上下に分かれた縦棒",sect:"節記号",uml:"分音記号(ウムラウト)",copy:"著作権表示記号",ordf:"女性序数標識",laquo:" 始め二重山括弧引用記号",not:"論理否定記号",reg:"登録商標記号",macr:"長音符",deg:"度記号",sup2:"上つき2, 2乗",sup3:"上つき3, 3乗",acute:"揚音符",micro:"ミクロン記号",para:"段落記号",middot:"中黒",cedil:"セディラ",sup1:"上つき1",ordm:"男性序数標識",raquo:"終わり二重山括弧引用記号", 6 | frac14:"四分の一",frac12:"二分の一",frac34:"四分の三",iquest:"逆疑問符",Agrave:"抑音符つき大文字A",Aacute:"揚音符つき大文字A",Acirc:"曲折アクセントつき大文字A",Atilde:"チルダつき大文字A",Auml:"分音記号つき大文字A",Aring:"リングつき大文字A",AElig:"AとEの合字",Ccedil:"セディラつき大文字C",Egrave:"抑音符つき大文字E",Eacute:"揚音符つき大文字E",Ecirc:"曲折アクセントつき大文字E",Euml:"分音記号つき大文字E",Igrave:"抑音符つき大文字I",Iacute:"揚音符つき大文字I",Icirc:"曲折アクセントつき大文字I",Iuml:"分音記号つき大文字I",ETH:"[アイスランド語]大文字ETH",Ntilde:"チルダつき大文字N",Ograve:"抑音符つき大文字O",Oacute:"揚音符つき大文字O",Ocirc:"曲折アクセントつき大文字O",Otilde:"チルダつき大文字O",Ouml:" 分音記号つき大文字O", 7 | times:"乗算記号",Oslash:"打ち消し線つき大文字O",Ugrave:"抑音符つき大文字U",Uacute:"揚音符つき大文字U",Ucirc:"曲折アクセントつき大文字U",Uuml:"分音記号つき大文字U",Yacute:"揚音符つき大文字Y",THORN:"[アイスランド語]大文字THORN",szlig:"ドイツ語エスツェット",agrave:"抑音符つき小文字a",aacute:"揚音符つき小文字a",acirc:"曲折アクセントつき小文字a",atilde:"チルダつき小文字a",auml:"分音記号つき小文字a",aring:"リングつき小文字a",aelig:"aとeの合字",ccedil:"セディラつき小文字c",egrave:"抑音符つき小文字e",eacute:"揚音符つき小文字e",ecirc:"曲折アクセントつき小文字e",euml:"分音記号つき小文字e",igrave:"抑音符つき小文字i",iacute:"揚音符つき小文字i",icirc:"曲折アクセントつき小文字i",iuml:"分音記号つき小文字i",eth:"アイスランド語小文字eth", 8 | ntilde:"チルダつき小文字n",ograve:"抑音符つき小文字o",oacute:"揚音符つき小文字o",ocirc:"曲折アクセントつき小文字o",otilde:"チルダつき小文字o",ouml:"分音記号つき小文字o",divide:"除算記号",oslash:"打ち消し線つき小文字o",ugrave:"抑音符つき小文字u",uacute:"揚音符つき小文字u",ucirc:"曲折アクセントつき小文字u",uuml:"分音記号つき小文字u",yacute:"揚音符つき小文字y",thorn:"アイスランド語小文字thorn",yuml:"分音記号つき小文字y",OElig:"OとEの合字",oelig:"oとeの合字",372:"曲折アクセントつき大文字W",374:"曲折アクセントつき大文字Y",373:"曲折アクセントつき小文字w",375:"曲折アクセントつき小文字y",sbquo:"シングル下引用符",8219:"左右逆の左引用符",bdquo:"ダブル下引用符",hellip:"三点リーダ",trade:"商標記号",9658:"右黒三角ポインタ",bull:"黒丸", 9 | rarr:"右矢印",rArr:"右二重矢印",hArr:"左右二重矢印",diams:"ダイヤ",asymp:"漸近"}); -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/specialchar/dialogs/lang/ko.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang("specialchar","ko",{euro:"유로화 기호",lsquo:"왼쪽 외 따옴표",rsquo:"오른쪽 외 따옴표",ldquo:"왼쪽 쌍 따옴표",rdquo:"오른쪽 쌍 따옴표",ndash:"반각 대시",mdash:"전각 대시",iexcl:"반전된 느낌표",cent:"센트 기호",pound:"파운드화 기호",curren:"커런시 기호",yen:"위안화 기호",brvbar:"파선",sect:"섹션 기호",uml:"분음 부호",copy:"저작권 기호",ordf:"Feminine ordinal indicator",laquo:"왼쪽 쌍꺽쇠 인용 부호",not:"금지 기호",reg:"등록 기호",macr:"장음 기호",deg:"도 기호",sup2:"위첨자 2",sup3:"위첨자 3",acute:"양음 악센트 부호",micro:"마이크로 기호",para:"단락 기호",middot:"가운데 점",cedil:"세디유",sup1:"위첨자 1",ordm:"Masculine ordinal indicator", 6 | raquo:"오른쪽 쌍꺽쇠 인용 부호",frac14:"분수 사분의 일",frac12:"분수 이분의 일",frac34:"분수 사분의 삼",iquest:"뒤집힌 물음표",Agrave:"억음 부호가 있는 라틴 대문자 A",Aacute:"양음 악센트 부호가 있는 라틴 대문자 A",Acirc:"곡절 악센트 부호가 있는 라틴 대문자 A",Atilde:"틸데가 있는 라틴 대문자 A",Auml:"분음 기호가 있는 라틴 대문자 A",Aring:"윗고리가 있는 라틴 대문자 A",AElig:"라틴 대문자 Æ",Ccedil:"세디유가 있는 라틴 대문자 C",Egrave:"억음 부호가 있는 라틴 대문자 E",Eacute:"양음 악센트 부호가 있는 라틴 대문자 E",Ecirc:"곡절 악센트 부호가 있는 라틴 대문자 E",Euml:"분음 기호가 있는 라틴 대문자 E",Igrave:"억음 부호가 있는 라틴 대문자 I",Iacute:"양음 악센트 부호가 있는 라틴 대문자 I",Icirc:"곡절 악센트 부호가 있는 라틴 대문자 I", 7 | Iuml:"분음 기호가 있는 라틴 대문자 I",ETH:"라틴 대문자 Eth",Ntilde:"틸데가 있는 라틴 대문자 N",Ograve:"억음 부호가 있는 라틴 대문자 O",Oacute:"양음 부호가 있는 라틴 대문자 O",Ocirc:"곡절 악센트 부호가 있는 라틴 대문자 O",Otilde:"틸데가 있는 라틴 대문자 O",Ouml:"분음 기호가 있는 라틴 대문자 O",times:"곱하기 기호",Oslash:"사선이 있는 라틴 대문자 O",Ugrave:"억음 부호가 있는 라틴 대문자 U",Uacute:"양음 부호가 있는 라틴 대문자 U",Ucirc:"곡절 악센트 부호가 있는 라틴 대문자 U",Uuml:"분음 기호가 있는 라틴 대문자 U",Yacute:"양음 부호가 있는 라틴 대문자 Y",THORN:"라틴 대문자 Thorn",szlig:"라틴 소문자 sharp s",agrave:"억음 부호가 있는 라틴 소문자 a",aacute:"양음 부호가 있는 라틴 소문자 a",acirc:"곡절 악센트 부호가 있는 라틴 소문자 a", 8 | atilde:"틸데가 있는 라틴 소문자 a",auml:"분음 기호가 있는 라틴 소문자 a",aring:"윗고리가 있는 라틴 소문자 a",aelig:"라틴 소문자 æ",ccedil:"세디유가 있는 라틴 소문자 c",egrave:"억음 부호가 있는 라틴 소문자 e",eacute:"양음 부호가 있는 라틴 소문자 e",ecirc:"곡절 악센트 부호가 있는 라틴 소문자 e",euml:"분음 기호가 있는 라틴 소문자 e",igrave:"억음 부호가 있는 라틴 소문자 i",iacute:"양음 부호가 있는 라틴 소문자 i",icirc:"곡절 악센트 부호가 있는 라틴 소문자 i",iuml:"분음 기호가 있는 라틴 소문자 i",eth:"라틴 소문자 eth",ntilde:"틸데가 있는 라틴 소문자 n",ograve:"억음 부호가 있는 라틴 소문자 o",oacute:"양음 부호가 있는 라틴 소문자 o",ocirc:"곡절 악센트 부호가 있는 라틴 소문자 o",otilde:"틸데가 있는 라틴 소문자 o",ouml:"분음 기호가 있는 라틴 소문자 o", 9 | divide:"나누기 기호",oslash:"사선이 있는 라틴 소문자 o",ugrave:"억음 부호가 있는 라틴 소문자 u",uacute:"양음 부호가 있는 라틴 소문자 u",ucirc:"곡절 악센트 부호가 있는 라틴 소문자 u",uuml:"분음 기호가 있는 라틴 소문자 u",yacute:"양음 부호가 있는 라틴 소문자 y",thorn:"라틴 소문자 thorn",yuml:"분음 기호가 있는 라틴 소문자 y",OElig:"라틴 대문합자 OE",oelig:"라틴 소문합자 oe",372:"곡절 악센트 부호가 있는 라틴 대문자 W",374:"곡절 악센트 부호가 있는 라틴 대문자 Y",373:"곡절 악센트 부호가 있는 라틴 소문자 w",375:"곡절 악센트 부호가 있는 라틴 소문자 y",sbquo:"외 아래-9 인용 부호",8219:"외 위쪽-뒤집힌-9 인용 부호",bdquo:"쌍 아래-9 인용 부호",hellip:"수평 생략 부호",trade:"상표 기호",9658:"검정 오른쪽 포인터",bull:"큰 점", 10 | rarr:"오른쪽 화살표",rArr:"오른쪽 두 줄 화살표",hArr:"양쪽 두 줄 화살표",diams:"검정 다이아몬드",asymp:"근사"}); -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/specialchar/dialogs/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang("specialchar","zh-cn",{euro:"欧元符号",lsquo:"左单引号",rsquo:"右单引号",ldquo:"左双引号",rdquo:"右双引号",ndash:"短划线",mdash:"长划线",iexcl:"竖翻叹号",cent:"分币符号",pound:"英镑符号",curren:"货币符号",yen:"日元符号",brvbar:"间断条",sect:"节标记",uml:"分音符",copy:"版权所有标记",ordf:"阴性顺序指示符",laquo:"左指双尖引号",not:"非标记",reg:"注册标记",macr:"长音符",deg:"度标记",sup2:"上标二",sup3:"上标三",acute:"锐音符",micro:"微符",para:"段落标记",middot:"中间点",cedil:"下加符",sup1:"上标一",ordm:"阳性顺序指示符",raquo:"右指双尖引号",frac14:"普通分数四分之一",frac12:"普通分数二分之一",frac34:"普通分数四分之三",iquest:"竖翻问号", 6 | Agrave:"带抑音符的拉丁文大写字母 A",Aacute:"带锐音符的拉丁文大写字母 A",Acirc:"带扬抑符的拉丁文大写字母 A",Atilde:"带颚化符的拉丁文大写字母 A",Auml:"带分音符的拉丁文大写字母 A",Aring:"带上圆圈的拉丁文大写字母 A",AElig:"拉丁文大写字母 Æ",Ccedil:"带下加符的拉丁文大写字母 C",Egrave:"带抑音符的拉丁文大写字母 E",Eacute:"带锐音符的拉丁文大写字母 E",Ecirc:"带扬抑符的拉丁文大写字母 E",Euml:"带分音符的拉丁文大写字母 E",Igrave:"带抑音符的拉丁文大写字母 I",Iacute:"带锐音符的拉丁文大写字母 I",Icirc:"带扬抑符的拉丁文大写字母 I",Iuml:"带分音符的拉丁文大写字母 I",ETH:"拉丁文大写字母 Eth",Ntilde:"带颚化符的拉丁文大写字母 N",Ograve:"带抑音符的拉丁文大写字母 O",Oacute:"带锐音符的拉丁文大写字母 O",Ocirc:"带扬抑符的拉丁文大写字母 O",Otilde:"带颚化符的拉丁文大写字母 O", 7 | Ouml:"带分音符的拉丁文大写字母 O",times:"乘号",Oslash:"带粗线的拉丁文大写字母 O",Ugrave:"带抑音符的拉丁文大写字母 U",Uacute:"带锐音符的拉丁文大写字母 U",Ucirc:"带扬抑符的拉丁文大写字母 U",Uuml:"带分音符的拉丁文大写字母 U",Yacute:"带抑音符的拉丁文大写字母 Y",THORN:"拉丁文大写字母 Thorn",szlig:"拉丁文小写字母清音 S",agrave:"带抑音符的拉丁文小写字母 A",aacute:"带锐音符的拉丁文小写字母 A",acirc:"带扬抑符的拉丁文小写字母 A",atilde:"带颚化符的拉丁文小写字母 A",auml:"带分音符的拉丁文小写字母 A",aring:"带上圆圈的拉丁文小写字母 A",aelig:"拉丁文小写字母 Ae",ccedil:"带下加符的拉丁文小写字母 C",egrave:"带抑音符的拉丁文小写字母 E",eacute:"带锐音符的拉丁文小写字母 E",ecirc:"带扬抑符的拉丁文小写字母 E",euml:"带分音符的拉丁文小写字母 E",igrave:"带抑音符的拉丁文小写字母 I", 8 | iacute:"带锐音符的拉丁文小写字母 I",icirc:"带扬抑符的拉丁文小写字母 I",iuml:"带分音符的拉丁文小写字母 I",eth:"拉丁文小写字母 Eth",ntilde:"带颚化符的拉丁文小写字母 N",ograve:"带抑音符的拉丁文小写字母 O",oacute:"带锐音符的拉丁文小写字母 O",ocirc:"带扬抑符的拉丁文小写字母 O",otilde:"带颚化符的拉丁文小写字母 O",ouml:"带分音符的拉丁文小写字母 O",divide:"除号",oslash:"带粗线的拉丁文小写字母 O",ugrave:"带抑音符的拉丁文小写字母 U",uacute:"带锐音符的拉丁文小写字母 U",ucirc:"带扬抑符的拉丁文小写字母 U",uuml:"带分音符的拉丁文小写字母 U",yacute:"带抑音符的拉丁文小写字母 Y",thorn:"拉丁文小写字母 Thorn",yuml:"带分音符的拉丁文小写字母 Y",OElig:"拉丁文大写连字 Oe",oelig:"拉丁文小写连字 Oe",372:"带扬抑符的拉丁文大写字母 W",374:"带扬抑符的拉丁文大写字母 Y", 9 | 373:"带扬抑符的拉丁文小写字母 W",375:"带扬抑符的拉丁文小写字母 Y",sbquo:"单下 9 形引号",8219:"单高横翻 9 形引号",bdquo:"双下 9 形引号",hellip:"水平省略号",trade:"商标标志",9658:"实心右指指针",bull:"加重号",rarr:"向右箭头",rArr:"向右双线箭头",hArr:"左右双线箭头",diams:"实心方块纸牌",asymp:"约等于"}); -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/specialchar/dialogs/lang/zh.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | CKEDITOR.plugins.setLang("specialchar","zh",{euro:"歐元符號",lsquo:"左單引號",rsquo:"右單引號",ldquo:"左雙引號",rdquo:"右雙引號",ndash:"短破折號",mdash:"長破折號",iexcl:"倒置的驚嘆號",cent:"美分符號",pound:"英鎊符號",curren:"貨幣符號",yen:"日圓符號",brvbar:"破折號",sect:"章節符號",uml:"分音符號",copy:"版權符號",ordf:"雌性符號",laquo:"左雙角括號",not:"Not 符號",reg:"註冊商標符號",macr:"長音符號",deg:"度數符號",sup2:"上標字 2",sup3:"上標字 3",acute:"尖音符號",micro:"微",para:"段落符號",middot:"中間點",cedil:"字母 C 下面的尾型符號 ",sup1:"上標",ordm:"雄性符號",raquo:"右雙角括號",frac14:"四分之一符號",frac12:"二分之一符號",frac34:"四分之三符號", 6 | iquest:"倒置的問號",Agrave:"拉丁大寫字母 A 帶抑音符號",Aacute:"拉丁大寫字母 A 帶尖音符號",Acirc:"拉丁大寫字母 A 帶揚抑符",Atilde:"拉丁大寫字母 A 帶波浪號",Auml:"拉丁大寫字母 A 帶分音符號",Aring:"拉丁大寫字母 A 帶上圓圈",AElig:"拉丁大寫字母 Æ",Ccedil:"拉丁大寫字母 C 帶下尾符號",Egrave:"拉丁大寫字母 E 帶抑音符號",Eacute:"拉丁大寫字母 E 帶尖音符號",Ecirc:"拉丁大寫字母 E 帶揚抑符",Euml:"拉丁大寫字母 E 帶分音符號",Igrave:"拉丁大寫字母 I 帶抑音符號",Iacute:"拉丁大寫字母 I 帶尖音符號",Icirc:"拉丁大寫字母 I 帶揚抑符",Iuml:"拉丁大寫字母 I 帶分音符號",ETH:"拉丁大寫字母 Eth",Ntilde:"拉丁大寫字母 N 帶波浪號",Ograve:"拉丁大寫字母 O 帶抑音符號",Oacute:"拉丁大寫字母 O 帶尖音符號",Ocirc:"拉丁大寫字母 O 帶揚抑符",Otilde:"拉丁大寫字母 O 帶波浪號", 7 | Ouml:"拉丁大寫字母 O 帶分音符號",times:"乘號",Oslash:"拉丁大寫字母 O 帶粗線符號",Ugrave:"拉丁大寫字母 U 帶抑音符號",Uacute:"拉丁大寫字母 U 帶尖音符號",Ucirc:"拉丁大寫字母 U 帶揚抑符",Uuml:"拉丁大寫字母 U 帶分音符號",Yacute:"拉丁大寫字母 Y 帶尖音符號",THORN:"拉丁大寫字母 Thorn",szlig:"拉丁小寫字母 s",agrave:"拉丁小寫字母 a 帶抑音符號",aacute:"拉丁小寫字母 a 帶尖音符號",acirc:"拉丁小寫字母 a 帶揚抑符",atilde:"拉丁小寫字母 a 帶波浪號",auml:"拉丁小寫字母 a 帶分音符號",aring:"拉丁小寫字母 a 帶上圓圈",aelig:"拉丁小寫字母 æ",ccedil:"拉丁小寫字母 c 帶下尾符號",egrave:"拉丁小寫字母 e 帶抑音符號",eacute:"拉丁小寫字母 e 帶尖音符號",ecirc:"拉丁小寫字母 e 帶揚抑符",euml:"拉丁小寫字母 e 帶分音符號",igrave:"拉丁小寫字母 i 帶抑音符號", 8 | iacute:"拉丁小寫字母 i 帶尖音符號",icirc:"拉丁小寫字母 i 帶揚抑符",iuml:"拉丁小寫字母 i 帶分音符號",eth:"拉丁小寫字母 eth",ntilde:"拉丁小寫字母 n 帶波浪號",ograve:"拉丁小寫字母 o 帶抑音符號",oacute:"拉丁小寫字母 o 帶尖音符號",ocirc:"拉丁小寫字母 o 帶揚抑符",otilde:"拉丁小寫字母 o 帶波浪號",ouml:"拉丁小寫字母 o 帶分音符號",divide:"除號",oslash:"拉丁小寫字母 o 帶粗線符號",ugrave:"拉丁小寫字母 u 帶抑音符號",uacute:"拉丁小寫字母 u 帶尖音符號",ucirc:"拉丁小寫字母 u 帶揚抑符",uuml:"拉丁小寫字母 u 帶分音符號",yacute:"拉丁小寫字母 y 帶尖音符號",thorn:"拉丁小寫字母 thorn",yuml:"拉丁小寫字母 y 帶分音符號",OElig:"拉丁大寫字母 OE",oelig:"拉丁小寫字母 oe",372:"拉丁大寫字母 W 帶揚抑符",374:"拉丁大寫字母 Y 帶揚抑符",373:"拉丁小寫字母 w 帶揚抑符", 9 | 375:"拉丁小寫字母 y 帶揚抑符",sbquo:"低 9 單引號",8219:"高 9 反轉單引號",bdquo:"低 9 雙引號",hellip:"水平刪節號",trade:"商標符號",9658:"黑色向右指箭號",bull:"項目符號",rarr:"向右箭號",rArr:"向右雙箭號",hArr:"左右雙箭號",diams:"黑鑽套裝",asymp:"約等於"}); -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/tableselection/styles/tableselection.css: -------------------------------------------------------------------------------- 1 | .cke_table-faked-selection-editor *::selection, table[data-cke-table-faked-selection-table] *::selection { 2 | background: transparent; 3 | } 4 | 5 | .cke_table-faked-selection-editor { 6 | /* With love, dedicated for Chrome, until https://bugs.chromium.org/p/chromium/issues/detail?id=702610 is resolved. 7 | It will force repaint (without reflow) so that selection is properly displayed. */ 8 | transform: translateZ( 0 ); 9 | } 10 | 11 | .cke_table-faked-selection { 12 | background: darkgray !important; 13 | color: black; 14 | } 15 | .cke_table-faked-selection a { 16 | color: black; 17 | } 18 | .cke_editable:focus .cke_table-faked-selection { 19 | /* We have to use !important here, as td might specify it's own background, thus table selection 20 | would not be visible. */ 21 | background: #0076cb !important; 22 | color: white; 23 | } 24 | .cke_editable:focus .cke_table-faked-selection a { 25 | color: white; 26 | } 27 | .cke_table-faked-selection::-moz-selection, .cke_table-faked-selection ::-moz-selection { 28 | background: transparent; 29 | } 30 | .cke_table-faked-selection::selection, .cke_table-faked-selection ::selection { 31 | background: transparent; 32 | } 33 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/widget/images/handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/plugins/widget/images/handle.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/wsc/LICENSE.md: -------------------------------------------------------------------------------- 1 | Software License Agreement 2 | ========================== 3 | 4 | **CKEditor WSC Plugin** 5 | Copyright © 2012, [CKSource](http://cksource.com) - Frederico Knabben. All rights reserved. 6 | 7 | Licensed under the terms of any of the following licenses at your choice: 8 | 9 | * GNU General Public License Version 2 or later (the "GPL"): 10 | http://www.gnu.org/licenses/gpl.html 11 | 12 | * GNU Lesser General Public License Version 2.1 or later (the "LGPL"): 13 | http://www.gnu.org/licenses/lgpl.html 14 | 15 | * Mozilla Public License Version 1.1 or later (the "MPL"): 16 | http://www.mozilla.org/MPL/MPL-1.1.html 17 | 18 | You are not required to, but if you want to explicitly declare the license you have chosen to be bound to when using, reproducing, modifying and distributing this software, just include a text file titled "legal.txt" in your version of this software, indicating your license choice. 19 | 20 | Sources of Intellectual Property Included in this plugin 21 | -------------------------------------------------------- 22 | 23 | Where not otherwise indicated, all plugin content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, the plugin will incorporate work done by developers outside of CKSource with their express permission. 24 | 25 | Trademarks 26 | ---------- 27 | 28 | CKEditor is a trademark of CKSource - Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders. 29 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/wsc/README.md: -------------------------------------------------------------------------------- 1 | CKEditor WebSpellChecker Plugin 2 | =============================== 3 | 4 | This plugin brings Web Spell Checker (WSC) into CKEditor. 5 | 6 | WSC is "installation-less", using the web-services of [WebSpellChecker.net](http://www.webspellchecker.net/). It's an out of the box solution. 7 | 8 | Installation 9 | ------------ 10 | 11 | 1. Clone/copy this repository contents in a new "plugins/wsc" folder in your CKEditor installation. 12 | 2. Enable the "wsc" plugin in the CKEditor configuration file (config.js): 13 | 14 | config.extraPlugins = 'wsc'; 15 | 16 | That's all. WSC will appear on the editor toolbar and will be ready to use. 17 | 18 | License 19 | ------- 20 | 21 | Licensed under the terms of any of the following licenses at your choice: [GPL](http://www.gnu.org/licenses/gpl.html), [LGPL](http://www.gnu.org/licenses/lgpl.html) and [MPL](http://www.mozilla.org/MPL/MPL-1.1.html). 22 | 23 | See LICENSE.md for more information. 24 | 25 | Developed in cooperation with [WebSpellChecker.net](http://www.webspellchecker.net/). 26 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/wsc/dialogs/ciframe.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 64 | 65 |

66 | 67 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/wsc/dialogs/tmpFrameset.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/wsc/dialogs/wsc.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.html or http://ckeditor.com/license 4 | */ 5 | 6 | html, body 7 | { 8 | background-color: transparent; 9 | margin: 0px; 10 | padding: 0px; 11 | } 12 | 13 | body 14 | { 15 | padding: 10px; 16 | } 17 | 18 | body, td, input, select, textarea 19 | { 20 | font-size: 11px; 21 | font-family: 'Microsoft Sans Serif' , Arial, Helvetica, Verdana; 22 | } 23 | 24 | .midtext 25 | { 26 | padding:0px; 27 | margin:10px; 28 | } 29 | 30 | .midtext p 31 | { 32 | padding:0px; 33 | margin:10px; 34 | } 35 | 36 | .Button 37 | { 38 | border: #737357 1px solid; 39 | color: #3b3b1f; 40 | background-color: #c7c78f; 41 | } 42 | 43 | .PopupTabArea 44 | { 45 | color: #737357; 46 | background-color: #e3e3c7; 47 | } 48 | 49 | .PopupTitleBorder 50 | { 51 | border-bottom: #d5d59d 1px solid; 52 | } 53 | .PopupTabEmptyArea 54 | { 55 | padding-left: 10px; 56 | border-bottom: #d5d59d 1px solid; 57 | } 58 | 59 | .PopupTab, .PopupTabSelected 60 | { 61 | border-right: #d5d59d 1px solid; 62 | border-top: #d5d59d 1px solid; 63 | border-left: #d5d59d 1px solid; 64 | padding: 3px 5px 3px 5px; 65 | color: #737357; 66 | } 67 | 68 | .PopupTab 69 | { 70 | margin-top: 1px; 71 | border-bottom: #d5d59d 1px solid; 72 | cursor: pointer; 73 | } 74 | 75 | .PopupTabSelected 76 | { 77 | font-weight: bold; 78 | cursor: default; 79 | padding-top: 4px; 80 | border-bottom: #f1f1e3 1px solid; 81 | background-color: #f1f1e3; 82 | } 83 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/plugins/wsc/skins/moono-lisa/wsc.css: -------------------------------------------------------------------------------- 1 | .cke_dialog_body #overlayBlock, 2 | .cke_dialog_body #no_check_over 3 | { 4 | top: 39px !important; 5 | } 6 | 7 | div[name=SpellTab] .wsc-spelltab-bottom .cke_dialog_ui_vbox td > .cke_dialog_ui_button:first-child 8 | { 9 | margin-top: 4px; 10 | } 11 | 12 | div[name=SpellTab] .wsc-spelltab-bottom .cke_dialog_ui_hbox_first .cke_dialog_ui_select > label 13 | { 14 | margin-left: 0; 15 | } 16 | 17 | div[name=SpellTab] .wsc-spelltab-bottom .cke_dialog_ui_hbox_first .cke_dialog_ui_select div.cke_dialog_ui_input_select 18 | { 19 | width: 140px !important; 20 | } 21 | 22 | div[name=SpellTab] .wsc-spelltab-bottom .cke_dialog_ui_hbox_first .cke_dialog_ui_select select.cke_dialog_ui_input_select, 23 | div[name=Thesaurus] div.cke_dialog_ui_input_select select.cke_dialog_ui_input_select 24 | { 25 | margin-top: 1px; 26 | } 27 | 28 | div[name=SpellTab] .wsc-spelltab-bottom .cke_dialog_ui_hbox_first .cke_dialog_ui_select select.cke_dialog_ui_input_select:focus, 29 | div[name=Thesaurus] div.cke_dialog_ui_input_select select.cke_dialog_ui_input_select:focus 30 | { 31 | margin-top: 0; 32 | } 33 | 34 | div[name=GrammTab] .cke_dialog_ui_vbox tbody > tr:first-child .cke_dialog_ui_button, 35 | div[name=Thesaurus] .cke_dialog_ui_vbox tbody > tr:first-child .cke_dialog_ui_button 36 | { 37 | margin-top: 4px !important; 38 | } 39 | 40 | div[name=Thesaurus] div.cke_dialog_ui_input_select 41 | { 42 | width: 180px !important; 43 | } 44 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/img/github-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/samples/img/github-top.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/img/header-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/samples/img/header-bg.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/img/header-separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/samples/img/header-separator.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/samples/img/logo.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/img/navigation-tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/samples/img/navigation-tip.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/js/sample.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | 6 | /* exported initSample */ 7 | 8 | if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) 9 | CKEDITOR.tools.enableHtml5Elements( document ); 10 | 11 | // The trick to keep the editor in the sample quite small 12 | // unless user specified own height. 13 | CKEDITOR.config.height = 150; 14 | CKEDITOR.config.width = 'auto'; 15 | 16 | var initSample = ( function() { 17 | var wysiwygareaAvailable = isWysiwygareaAvailable(), 18 | isBBCodeBuiltIn = !!CKEDITOR.plugins.get( 'bbcode' ); 19 | 20 | return function() { 21 | var editorElement = CKEDITOR.document.getById( 'editor' ); 22 | 23 | // :((( 24 | if ( isBBCodeBuiltIn ) { 25 | editorElement.setHtml( 26 | 'Hello world!\n\n' + 27 | 'I\'m an instance of [url=https://ckeditor.com]CKEditor[/url].' 28 | ); 29 | } 30 | 31 | // Depending on the wysiwygare plugin availability initialize classic or inline editor. 32 | if ( wysiwygareaAvailable ) { 33 | CKEDITOR.replace( 'editor' ); 34 | } else { 35 | editorElement.setAttribute( 'contenteditable', 'true' ); 36 | CKEDITOR.inline( 'editor' ); 37 | 38 | // TODO we can consider displaying some info box that 39 | // without wysiwygarea the classic editor may not work. 40 | } 41 | }; 42 | 43 | function isWysiwygareaAvailable() { 44 | // If in development mode, then the wysiwygarea must be available. 45 | // Split REV into two strings so builder does not replace it :D. 46 | if ( CKEDITOR.revision == ( '%RE' + 'V%' ) ) { 47 | return true; 48 | } 49 | 50 | return !!CKEDITOR.plugins.get( 'wysiwygarea' ); 51 | } 52 | } )(); 53 | 54 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/old/ajax.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Ajax — CKEditor Sample 10 | 11 | 12 | 40 | 41 | 42 |

43 | CKEditor Samples » Create and Destroy Editor Instances for Ajax Applications 44 |

45 |
46 | This sample is not maintained anymore. Check out its brand new version in CKEditor SDK. 47 |
48 |
49 |

50 | This sample shows how to create and destroy CKEditor instances on the fly. After the removal of CKEditor the content created inside the editing 51 | area will be displayed in a <div> element. 52 |

53 |

54 | For details of how to create this setup check the source code of this sample page 55 | for JavaScript code responsible for the creation and destruction of a CKEditor instance. 56 |

57 |
58 |

Click the buttons to create and remove a CKEditor instance.

59 |

60 | 61 | 62 |

63 | 64 |
65 |
66 | 74 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/old/appendto.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | Append To Page Element Using JavaScript Code — CKEditor Sample 10 | 11 | 12 | 13 | 14 |

15 | CKEditor Samples » Append To Page Element Using JavaScript Code 16 |

17 |
18 | This sample is not maintained anymore. Check out the brand new samples in CKEditor SDK. 19 |
20 |
21 |
22 |

23 | The CKEDITOR.appendTo() method serves to to place editors inside existing DOM elements. Unlike CKEDITOR.replace(), 24 | a target container to be replaced is no longer necessary. A new editor 25 | instance is inserted directly wherever it is desired. 26 |

27 |
CKEDITOR.appendTo( 'container_id',
28 | 	{ /* Configuration options to be used. */ }
29 | 	'Editor content to be used.'
30 | );
31 |
32 | 46 |
47 |
48 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/old/assets/inlineall/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/samples/old/assets/inlineall/logo.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/old/assets/posteddata.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | Sample — CKEditor 12 | 13 | 14 | 15 |

16 | CKEditor — Posted Data 17 |

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | $value ) 31 | { 32 | if ( ( !is_string($value) && !is_numeric($value) ) || !is_string($key) ) 33 | continue; 34 | 35 | if ( get_magic_quotes_gpc() ) 36 | $value = htmlspecialchars( stripslashes((string)$value) ); 37 | else 38 | $value = htmlspecialchars( (string)$value ); 39 | ?> 40 | 41 | 42 | 43 | 44 | 48 |
Field NameValue
49 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/old/assets/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/samples/old/assets/sample.jpg -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/old/assets/uilanguages/languages.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 3 | For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license 4 | */ 5 | var CKEDITOR_LANGS=function(){var c={af:"Afrikaans",ar:"Arabic",az:"Azerbaijani",bg:"Bulgarian",bn:"Bengali/Bangla",bs:"Bosnian",ca:"Catalan",cs:"Czech",cy:"Welsh",da:"Danish",de:"German","de-ch":"German (Switzerland)",el:"Greek",en:"English","en-au":"English (Australia)","en-ca":"English (Canadian)","en-gb":"English (United Kingdom)",eo:"Esperanto",es:"Spanish","es-mx":"Spanish (Mexico)",et:"Estonian",eu:"Basque",fa:"Persian",fi:"Finnish",fo:"Faroese",fr:"French","fr-ca":"French (Canada)",gl:"Galician", 6 | gu:"Gujarati",he:"Hebrew",hi:"Hindi",hr:"Croatian",hu:"Hungarian",id:"Indonesian",is:"Icelandic",it:"Italian",ja:"Japanese",ka:"Georgian",km:"Khmer",ko:"Korean",ku:"Kurdish",lt:"Lithuanian",lv:"Latvian",mk:"Macedonian",mn:"Mongolian",ms:"Malay",nb:"Norwegian Bokmal",nl:"Dutch",no:"Norwegian",oc:"Occitan",pl:"Polish",pt:"Portuguese (Portugal)","pt-br":"Portuguese (Brazil)",ro:"Romanian",ru:"Russian",si:"Sinhala",sk:"Slovak",sq:"Albanian",sl:"Slovenian",sr:"Serbian (Cyrillic)","sr-latn":"Serbian (Latin)", 7 | sv:"Swedish",th:"Thai",tr:"Turkish",tt:"Tatar",ug:"Uighur",uk:"Ukrainian",vi:"Vietnamese",zh:"Chinese Traditional","zh-cn":"Chinese Simplified"},b=[],a;for(a in CKEDITOR.lang.languages)b.push({code:a,name:c[a]||a});b.sort(function(a,b){return a.name' + requires[ i ] + '' ); 22 | } 23 | 24 | if ( missing.length ) { 25 | var warn = CKEDITOR.dom.element.createFromHtml( 26 | '
' + 27 | 'To fully experience this demo, the ' + missing.join( ', ' ) + ' plugin' + ( missing.length > 1 ? 's are' : ' is' ) + ' required.' + 28 | '
' 29 | ); 30 | warn.insertBefore( editor.container ); 31 | } 32 | } 33 | 34 | // Set icons. 35 | var doc = new CKEDITOR.dom.document( document ), 36 | icons = doc.find( '.button_icon' ); 37 | 38 | for ( i = 0; i < icons.count(); i++ ) { 39 | var icon = icons.getItem( i ), 40 | name = icon.getAttribute( 'data-icon' ), 41 | style = CKEDITOR.skin.getIconStyle( name, ( CKEDITOR.lang.dir == 'rtl' ) ); 42 | 43 | icon.addClass( 'cke_button_icon' ); 44 | icon.addClass( 'cke_button__' + name + '_icon' ); 45 | icon.setAttribute( 'style', style ); 46 | icon.setStyle( 'float', 'none' ); 47 | 48 | } 49 | } ); 50 | } )(); 51 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/old/sample_posteddata.php: -------------------------------------------------------------------------------- 1 |
 2 | 
 3 | -------------------------------------------------------------------------------------------
 4 |   CKEditor - Posted Data
 5 | 
 6 |   We are sorry, but your Web server does not support the PHP language used in this script.
 7 | 
 8 |   Please note that CKEditor can be used with any other server-side language than just PHP.
 9 |   To save the content created with CKEditor you need to read the POST data on the server
10 |   side and write it to a file or the database.
11 | 
12 |   Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
13 |   For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
14 | -------------------------------------------------------------------------------------------
15 | 
16 | 
*/ include "assets/posteddata.php"; ?> 17 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/old/tabindex.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | TAB Key-Based Navigation — CKEditor Sample 10 | 11 | 12 | 22 | 42 | 43 | 44 |

45 | CKEditor Samples » TAB Key-Based Navigation 46 |

47 |
48 | This sample is not maintained anymore. Check out its brand new version in CKEditor SDK. 49 |
50 |
51 |

52 | This sample shows how tab key navigation among editor instances is 53 | affected by the tabIndex attribute from 54 | the original page element. Use TAB key to move between the editors. 55 |

56 |
57 |

58 | 59 |

60 |
61 |

62 | 63 |

64 |

65 | 66 |

67 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/old/uicolor.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | UI Color Picker — CKEditor Sample 10 | 11 | 12 | 13 | 14 |

15 | CKEditor Samples » UI Color 16 |

17 |
18 | This sample is not maintained anymore. Check out its brand new version in CKEditor SDK. 19 |
20 |
21 |

22 | This sample shows how to automatically replace <textarea> elements 23 | with a CKEditor instance with an option to change the color of its user interface.
24 | Note:The UI skin color feature depends on the CKEditor skin 25 | compatibility. The Moono and Kama skins are examples of skins that work with it. 26 |

27 |
28 |
29 |

30 | This editor instance has a UI color value defined in configuration to change the skin color, 31 | To specify the color of the user interface, set the uiColor property: 32 |

33 |
34 | CKEDITOR.replace( 'textarea_id', {
35 | 	uiColor: '#14B8C4'
36 | });
37 |

38 | Note that textarea_id in the code above is the id attribute of 39 | the <textarea> element to be replaced. 40 |

41 |

42 | 43 | 56 |

57 |

58 | 59 |

60 |
61 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/toolbarconfigurator/css/fontello.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'fontello'; 3 | src: url('../font/fontello.eot?89024372'); 4 | src: url('../font/fontello.eot?89024372#iefix') format('embedded-opentype'), 5 | url('../font/fontello.woff?89024372') format('woff'), 6 | url('../font/fontello.ttf?89024372') format('truetype'), 7 | url('../font/fontello.svg?89024372#fontello') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 12 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 13 | /* 14 | @media screen and (-webkit-min-device-pixel-ratio:0) { 15 | @font-face { 16 | font-family: 'fontello'; 17 | src: url('../font/fontello.svg?89024372#fontello') format('svg'); 18 | } 19 | } 20 | */ 21 | 22 | [class^="icon-"]:before, [class*=" icon-"]:before { 23 | font-family: "fontello"; 24 | font-style: normal; 25 | font-weight: normal; 26 | speak: none; 27 | 28 | display: inline-block; 29 | text-decoration: inherit; 30 | width: 1em; 31 | margin-right: .2em; 32 | text-align: center; 33 | /* opacity: .8; */ 34 | 35 | /* For safety - reset parent styles, that can break glyph codes*/ 36 | font-variant: normal; 37 | text-transform: none; 38 | 39 | /* fix buttons height, for twitter bootstrap */ 40 | line-height: 1em; 41 | 42 | /* Animation center compensation - margins should be symmetric */ 43 | /* remove if not needed */ 44 | margin-left: .2em; 45 | 46 | /* you can be more comfortable with increased icons size */ 47 | /* font-size: 120%; */ 48 | 49 | /* Uncomment for 3D effect */ 50 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 51 | } 52 | 53 | .icon-trash:before { content: '\e802'; } /* '' */ 54 | .icon-down-big:before { content: '\e800'; } /* '' */ 55 | .icon-up-big:before { content: '\e801'; } /* '' */ 56 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/toolbarconfigurator/font/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Font license info 2 | 3 | 4 | ## Font Awesome 5 | 6 | Copyright (C) 2012 by Dave Gandy 7 | 8 | Author: Dave Gandy 9 | License: SIL () 10 | Homepage: http://fortawesome.github.com/Font-Awesome/ 11 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/toolbarconfigurator/font/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "css_prefix_text": "icon-", 4 | "css_use_suffix": false, 5 | "hinting": true, 6 | "units_per_em": 1000, 7 | "ascent": 850, 8 | "glyphs": [ 9 | { 10 | "uid": "f48ae54adfb27d8ada53d0fd9e34ee10", 11 | "css": "trash-empty", 12 | "code": 59392, 13 | "src": "fontawesome" 14 | }, 15 | { 16 | "uid": "1c4068ed75209e21af36017df8871802", 17 | "css": "down-big", 18 | "code": 59393, 19 | "src": "fontawesome" 20 | }, 21 | { 22 | "uid": "95376bf082bfec6ce06ea1cda7bd7ead", 23 | "css": "up-big", 24 | "code": 59394, 25 | "src": "fontawesome" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/toolbarconfigurator/font/fontello.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/samples/toolbarconfigurator/font/fontello.eot -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/toolbarconfigurator/font/fontello.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2014 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/toolbarconfigurator/font/fontello.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/samples/toolbarconfigurator/font/fontello.ttf -------------------------------------------------------------------------------- /greybook/static/ckeditor/samples/toolbarconfigurator/font/fontello.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/samples/toolbarconfigurator/font/fontello.woff -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/icons.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/icons_hidpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/icons_hidpi.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/images/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/images/arrow.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/images/close.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/images/hidpi/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/images/hidpi/close.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/images/hidpi/lock-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/images/hidpi/lock-open.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/images/hidpi/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/images/hidpi/lock.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/images/hidpi/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/images/hidpi/refresh.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/images/lock-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/images/lock-open.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/images/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/images/lock.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/images/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/images/refresh.png -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/ckeditor/skins/moono-lisa/images/spinner.gif -------------------------------------------------------------------------------- /greybook/static/ckeditor/skins/moono-lisa/readme.md: -------------------------------------------------------------------------------- 1 | "Moono-lisa" Skin 2 | ================= 3 | 4 | This skin has been made a **default skin** starting from CKEditor 4.6.0 and is maintained by the core developers. 5 | 6 | For more information about skins, please check the [CKEditor Skin SDK](https://docs.ckeditor.com/ckeditor4/docs/#!/guide/skin_sdk_intro) 7 | documentation. 8 | 9 | Features 10 | ------------------- 11 | "Moono-lisa" is a monochromatic skin, which offers a modern, flat and minimalistic look which blends very well in modern design. 12 | It comes with the following features: 13 | 14 | - Chameleon feature with brightness. 15 | - High-contrast compatibility. 16 | - Graphics source provided in SVG. 17 | 18 | Directory Structure 19 | ------------------- 20 | 21 | CSS parts: 22 | - **editor.css**: the main CSS file. It's simply loading several other files, for easier maintenance, 23 | - **mainui.css**: the file contains styles of entire editor outline structures, 24 | - **toolbar.css**: the file contains styles of the editor toolbar space (top), 25 | - **richcombo.css**: the file contains styles of the rich combo ui elements on toolbar, 26 | - **panel.css**: the file contains styles of the rich combo drop-down, it's not loaded 27 | until the first panel open up, 28 | - **elementspath.css**: the file contains styles of the editor elements path bar (bottom), 29 | - **menu.css**: the file contains styles of all editor menus including context menu and button drop-down, 30 | it's not loaded until the first menu open up, 31 | - **dialog.css**: the CSS files for the dialog UI, it's not loaded until the first dialog open, 32 | - **reset.css**: the file defines the basis of style resets among all editor UI spaces, 33 | - **preset.css**: the file defines the default styles of some UI elements reflecting the skin preference, 34 | - **editor_XYZ.css** and **dialog_XYZ.css**: browser specific CSS hacks. 35 | 36 | Other parts: 37 | - **skin.js**: the only JavaScript part of the skin that registers the skin, its browser specific files and its icons and defines the Chameleon feature, 38 | - **images/**: contains a fill general used images, 39 | - **dev/**: contains SVG and PNG source of the skin icons. 40 | 41 | License 42 | ------- 43 | 44 | Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. 45 | 46 | For licensing, see LICENSE.md or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license) 47 | -------------------------------------------------------------------------------- /greybook/static/css/main.css: -------------------------------------------------------------------------------- 1 | nav { 2 | margin-bottom: 20px; 3 | } 4 | 5 | img { 6 | max-width: 100%; 7 | height: auto !important; 8 | } 9 | 10 | .inline { 11 | display: inline; 12 | } 13 | 14 | .page-header { 15 | padding-top: 20px; 16 | padding-bottom: 20px; 17 | } 18 | 19 | .comments { 20 | margin: 20px 0; 21 | } 22 | 23 | .post-body { 24 | margin-bottom: 30px; 25 | } 26 | 27 | .reply-body { 28 | margin-top: 10px; 29 | } 30 | 31 | .sidebar { 32 | padding-left: 30px; 33 | } 34 | 35 | footer { 36 | margin-top: 30px; 37 | padding: 10px 0 40px; 38 | color: #999; 39 | border-top: 1px solid #e5e5e5; 40 | } 41 | 42 | .page-footer { 43 | padding-top: 40px; 44 | } 45 | 46 | .tip { 47 | /* from github.com */ 48 | color: black !important; 49 | position: relative; 50 | padding: 40px; 51 | text-align: center; 52 | background-color: #fafbfc; 53 | border: 1px solid #e1e4e8; 54 | border-radius: 3px; 55 | box-shadow: inset 0 0 10px rgba(27, 31, 35, 0.05); 56 | } 57 | -------------------------------------------------------------------------------- /greybook/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/greybook/static/favicon.ico -------------------------------------------------------------------------------- /greybook/static/js/dayjs/plugin/localizedFormat.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).dayjs_plugin_localizedFormat=t()}(this,(function(){"use strict";var e={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"};return function(t,o,n){var r=o.prototype,i=r.format;n.en.formats=e,r.format=function(t){void 0===t&&(t="YYYY-MM-DDTHH:mm:ssZ");var o=this.$locale().formats,n=function(t,o){return t.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,(function(t,n,r){var i=r&&r.toUpperCase();return n||o[r]||e[r]||o[i].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,(function(e,t,o){return t||o.slice(1)}))}))}(t,void 0===o?{}:o);return i.call(this,n)}}})); -------------------------------------------------------------------------------- /greybook/static/js/dayjs/plugin/relativeTime.js: -------------------------------------------------------------------------------- 1 | !function(r,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(r="undefined"!=typeof globalThis?globalThis:r||self).dayjs_plugin_relativeTime=e()}(this,(function(){"use strict";return function(r,e,t){r=r||{};var n=e.prototype,o={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"};function i(r,e,t,o){return n.fromToBase(r,e,t,o)}t.en.relativeTime=o,n.fromToBase=function(e,n,i,d,u){for(var f,a,s,l=i.$locale().relativeTime||o,h=r.thresholds||[{l:"s",r:44,d:"second"},{l:"m",r:89},{l:"mm",r:44,d:"minute"},{l:"h",r:89},{l:"hh",r:21,d:"hour"},{l:"d",r:35},{l:"dd",r:25,d:"day"},{l:"M",r:45},{l:"MM",r:10,d:"month"},{l:"y",r:17},{l:"yy",d:"year"}],m=h.length,c=0;c0,p<=y.r||!y.r){p<=1&&c>0&&(y=h[c-1]);var v=l[y.l];u&&(p=u(""+p)),a="string"==typeof v?v.replace("%d",p):v(p,n,y.l,s);break}}if(n)return a;var M=s?l.future:l.past;return"function"==typeof M?M(a):M.replace("%s",a)},n.to=function(r,e){return i(r,e,this,!0)},n.from=function(r,e){return i(r,e,this)};var d=function(r){return r.$u?t.utc():t()};n.toNow=function(r){return this.to(d(this),r)},n.fromNow=function(r){return this.from(d(this),r)}}})); -------------------------------------------------------------------------------- /greybook/static/js/dayjs/plugin/utc.js: -------------------------------------------------------------------------------- 1 | !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs_plugin_utc=i()}(this,(function(){"use strict";var t="minute",i=/[+-]\d\d(?::?\d\d)?/g,e=/([+-]|\d\d)/g;return function(s,f,n){var u=f.prototype;n.utc=function(t){var i={date:t,utc:!0,args:arguments};return new f(i)},u.utc=function(i){var e=n(this.toDate(),{locale:this.$L,utc:!0});return i?e.add(this.utcOffset(),t):e},u.local=function(){return n(this.toDate(),{locale:this.$L,utc:!1})};var o=u.parse;u.parse=function(t){t.utc&&(this.$u=!0),this.$utils().u(t.$offset)||(this.$offset=t.$offset),o.call(this,t)};var r=u.init;u.init=function(){if(this.$u){var t=this.$d;this.$y=t.getUTCFullYear(),this.$M=t.getUTCMonth(),this.$D=t.getUTCDate(),this.$W=t.getUTCDay(),this.$H=t.getUTCHours(),this.$m=t.getUTCMinutes(),this.$s=t.getUTCSeconds(),this.$ms=t.getUTCMilliseconds()}else r.call(this)};var a=u.utcOffset;u.utcOffset=function(s,f){var n=this.$utils().u;if(n(s))return this.$u?0:n(this.$offset)?a.call(this):this.$offset;if("string"==typeof s&&(s=function(t){void 0===t&&(t="");var s=t.match(i);if(!s)return null;var f=(""+s[0]).match(e)||["-",0,0],n=f[0],u=60*+f[1]+ +f[2];return 0===u?0:"+"===n?u:-u}(s),null===s))return this;var u=Math.abs(s)<=16?60*s:s,o=this;if(f)return o.$offset=u,o.$u=0===s,o;if(0!==s){var r=this.$u?this.toDate().getTimezoneOffset():-1*this.utcOffset();(o=this.local().add(u+r,t)).$offset=u,o.$x.$localOffset=r}else o=this.utc();return o};var h=u.format;u.format=function(t){var i=t||(this.$u?"YYYY-MM-DDTHH:mm:ss[Z]":"");return h.call(this,i)},u.valueOf=function(){var t=this.$utils().u(this.$offset)?0:this.$offset+(this.$x.$localOffset||this.$d.getTimezoneOffset());return this.$d.valueOf()-6e4*t},u.isUTC=function(){return!!this.$u},u.toISOString=function(){return this.toDate().toISOString()},u.toString=function(){return this.toDate().toUTCString()};var l=u.toDate;u.toDate=function(t){return"s"===t&&this.$offset?n(this.format("YYYY-MM-DD HH:mm:ss:SSS")).toDate():l.call(this)};var c=u.diff;u.diff=function(t,i,e){if(t&&this.$u===t.$u)return c.call(this,t,i,e);var s=this.local(),f=n(t).local();return c.call(s,f,i,e)}}})); -------------------------------------------------------------------------------- /greybook/static/js/main.js: -------------------------------------------------------------------------------- 1 | dayjs.extend(window.dayjs_plugin_relativeTime); 2 | dayjs.extend(window.dayjs_plugin_utc); 3 | dayjs.extend(window.dayjs_plugin_localizedFormat); 4 | 5 | const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); 6 | const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); 7 | 8 | function renderAllDatetime() { 9 | // render normal time 10 | const elements = document.querySelectorAll('.dayjs'); 11 | elements.forEach(elem => { 12 | const date = dayjs.utc(elem.innerHTML); 13 | const format = elem.dataset.format ?? 'LL'; 14 | elem.innerHTML = date.local().format(format); 15 | }); 16 | // render from now time 17 | const fromNowElements = document.querySelectorAll('.dayjs-from-now'); 18 | fromNowElements.forEach(elem => { 19 | const date = dayjs.utc(elem.innerHTML); 20 | elem.innerHTML = date.local().fromNow(); 21 | }); 22 | // render tooltip time 23 | const toolTipElements = document.querySelectorAll('.dayjs-tooltip'); 24 | toolTipElements.forEach(elem => { 25 | const date = dayjs.utc(elem.dataset.timestamp); 26 | const format = elem.dataset.format ?? 'LLL'; 27 | elem.dataset.bsTitle = date.local().format(format); 28 | const tooltip = new bootstrap.Tooltip(elem); 29 | }); 30 | } 31 | 32 | document.addEventListener('DOMContentLoaded', renderAllDatetime); 33 | -------------------------------------------------------------------------------- /greybook/templates/admin/edit_category.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/form.html' import render_form %} 3 | 4 | {% block title %}Edit Category{% endblock %} 5 | 6 | {% block content %} 7 | 10 |
11 |
12 | {{ render_form(form) }} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /greybook/templates/admin/edit_link.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/form.html' import render_form %} 3 | 4 | {% block title %}Edit Link{% endblock %} 5 | 6 | {% block content %} 7 | 10 |
11 |
12 | {{ render_form(form) }} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /greybook/templates/admin/edit_post.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/form.html' import render_form %} 3 | 4 | {% block title %}Edit Post{% endblock %} 5 | 6 | {% block content %} 7 | 10 | {{ render_form(form) }} 11 | {% endblock %} 12 | 13 | {% block scripts %} 14 | {{ super() }} 15 | 16 | {{ ckeditor.config(name='body') }} 17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /greybook/templates/admin/manage_category.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/form.html' import render_form %} 3 | {% from 'macros.html' import render_inline_form %} 4 | 5 | {% block title %}Manage Categories{% endblock %} 6 | 7 | {% block content %} 8 | 18 | {% if categories %} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {% for category in categories %} 28 | 29 | 34 | 35 | 48 | 49 | {% endfor %} 50 |
NamePostsActions
30 | 31 | {{ category.name }} 32 | 33 | {{ category.posts|length }} 36 | {% if category.id != 1 %} 37 | 39 | Edit 40 | 41 | {{ render_inline_form( 42 | action=url_for('.delete_category', category_id=category.id), 43 | button_style='outline-danger', 44 | button_text='Delete', 45 | ) }} 46 | {% endif %} 47 |
51 |

52 | Tips: Deleting a category does not delete the article under that category. 53 | The articles under this category will be moved to the default category. 54 |

55 | {% else %} 56 |
57 |
No categories.
58 |
59 | {% endif %} 60 | {% endblock %} 61 | -------------------------------------------------------------------------------- /greybook/templates/admin/manage_link.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/form.html' import render_form %} 3 | {% from 'macros.html' import render_inline_form %} 4 | 5 | {% block title %}Manage Links{% endblock %} 6 | 7 | {% block content %} 8 | 16 | {% if links %} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {% for link in links %} 26 | 27 | 28 | 29 | 38 | 39 | {% endfor %} 40 |
NameURLActions
{{ link.name }}{{ link.url }} 30 | Edit 32 | {{ render_inline_form( 33 | action=url_for('.delete_link', link_id=link.id), 34 | button_style='outline-danger', 35 | button_text='Delete', 36 | ) }} 37 |
41 | {% else %} 42 |
43 |
No links.
44 |
45 | {% endif %} 46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /greybook/templates/admin/manage_post.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/pagination.html' import render_pagination %} 3 | {% from 'macros.html' import render_inline_form %} 4 | 5 | {% block title %}Manage Posts{% endblock %} 6 | 7 | {% block content %} 8 | 18 | {% if posts %} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {% for post in posts %} 31 | 32 | 37 | 42 | 48 | 53 | 54 | 71 | 72 | {% endfor %} 73 |
TitleCategoryDateCommentsWordsActions
33 | 34 | {{ post.title }} 35 | 36 | 38 | 39 | {{ post.category.name }} 40 | 41 | 43 | Created at 44 | {{ post.created_at }}
45 | Updated at 46 | {{ post.updated_at }} 47 |
49 | 50 | {{ post.comments|length }} 51 | 52 | {{ post.body|striptags|length }} 55 | {{ render_inline_form( 56 | action=url_for('.set_comment', post_id=post.id, next=request.full_path), 57 | button_style='outline-warning' if post.can_comment else 'outline-success', 58 | button_text='Disable Comment' if post.can_comment else 'Enable Comment', 59 | confirm=None, 60 | ) }} 61 | 63 | Edit 64 | 65 | {{ render_inline_form( 66 | action=url_for('.delete_post', post_id=post.id, next=request.full_path), 67 | button_style='outline-danger', 68 | button_text='Delete', 69 | ) }} 70 |
74 | 77 | {% else %} 78 |
79 |
No posts.
80 |
81 | {% endif %} 82 | {% endblock %} 83 | -------------------------------------------------------------------------------- /greybook/templates/admin/new_category.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/form.html' import render_form %} 3 | 4 | {% block title %}New Category{% endblock %} 5 | 6 | {% block content %} 7 | 10 |
11 |
12 | {{ render_form(form) }} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /greybook/templates/admin/new_link.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/form.html' import render_form %} 3 | 4 | {% block title %}New Link{% endblock %} 5 | 6 | {% block content %} 7 | 10 |
11 |
12 | {{ render_form(form) }} 13 |
14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /greybook/templates/admin/new_post.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/form.html' import render_form %} 3 | 4 | {% block title %}New Post{% endblock %} 5 | 6 | {% block content %} 7 | 10 | {{ render_form(form) }} 11 | {% endblock %} 12 | 13 | {% block scripts %} 14 | {{ super() }} 15 | 16 | {{ ckeditor.config(name='body') }} 17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /greybook/templates/admin/settings.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/form.html' import render_form %} 3 | 4 | {% block title %}Settings{% endblock %} 5 | 6 | {% block content %} 7 | 10 | {{ render_form(form) }} 11 | {% endblock %} 12 | 13 | {% block scripts %} 14 | {{ super() }} 15 | 16 | {{ ckeditor.config(name='about') }} 17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /greybook/templates/auth/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/form.html' import render_form %} 3 | 4 | {% block title %}Login{% endblock %} 5 | 6 | {% block content %} 7 | 10 |
11 | {{ render_form(form, extra_classes='col-6') }} 12 |
13 | {% endblock %} 14 | 15 | {% block footer %}{% endblock %} 16 | -------------------------------------------------------------------------------- /greybook/templates/blog/_posts.html: -------------------------------------------------------------------------------- 1 | {% if posts %} 2 | {% for post in posts %} 3 |

4 | 5 | {{ post.title }} 6 | 7 |

8 |

9 | {{ post.body|striptags|truncate }} 10 | 11 | Read More 12 | 13 |

14 | 15 | Comments: 16 | 17 | {{ post.reviewed_comments_count }} 18 |    19 | Category: 20 | 21 | {{ post.category.name }} 22 | 23 | {{ post.created_at }} 24 | 25 | {% if not loop.last %} 26 |
27 | {% endif %} 28 | {% endfor %} 29 | {% else %} 30 |
31 |
No posts yet.
32 | {% if current_user.is_authenticated %} 33 | Write Now 34 | {% endif %} 35 |
36 | {% endif %} 37 | -------------------------------------------------------------------------------- /greybook/templates/blog/_sidebar.html: -------------------------------------------------------------------------------- 1 | {% if links %} 2 |
3 |
Links
4 | 13 |
14 | {% endif %} 15 | {% if categories %} 16 |
17 |
Categories
18 | 29 |
30 | {% endif %} 31 | 48 | -------------------------------------------------------------------------------- /greybook/templates/blog/about.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}About{% endblock %} 4 | 5 | {% block content %} 6 | 9 |
10 |
11 | {{ admin.about|safe }} 12 |
13 | 16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /greybook/templates/blog/category.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/pagination.html' import render_pagination %} 3 | 4 | {% block title %}{{ category.name }}{% endblock %} 5 | 6 | {% block content %} 7 | 13 |
14 |
15 | {% include "blog/_posts.html" %} 16 | 19 |
20 | 23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /greybook/templates/blog/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'bootstrap5/pagination.html' import render_pager %} 3 | 4 | {% block title %}Home{% endblock %} 5 | 6 | {% block content %} 7 | 11 |
12 |
13 | {% include 'blog/_posts.html' %} 14 | {% if posts %} 15 | 18 | {% endif %} 19 |
20 | 23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /greybook/templates/errors/400.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}400 Error{% endblock %} 4 | 5 | {% block content %} 6 | 9 |
10 |
11 |

{{ description|default('Bad Request') }}

12 |
13 | 16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /greybook/templates/errors/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}404 Error{% endblock %} 4 | 5 | {% block content %} 6 | 9 |
10 |
11 |

{{ description|default('Page Not Found') }}

12 |
13 | 16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /greybook/templates/errors/500.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block title %}500 Error{% endblock %} 4 | 5 | {% block content %} 6 | 9 |
10 |
11 |

{{ description|default('Internal Server Error') }}

12 |
13 | 16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /greybook/templates/macros.html: -------------------------------------------------------------------------------- 1 | {% macro render_inline_form(action, button_style, button_text, confirm='Are you sure?') %} 2 |
3 | 4 | 8 |
9 | {% endmacro %} 10 | 11 | {% macro pager(pagination, fragment='') %} 12 | 28 | {% endmacro %} 29 | -------------------------------------------------------------------------------- /greybook/utils.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | from pathlib import Path 3 | from urllib.parse import urljoin, urlparse 4 | 5 | from flask import current_app, redirect, request, url_for 6 | 7 | 8 | def is_safe_url(target): 9 | ref_url = urlparse(request.host_url) 10 | test_url = urlparse(urljoin(request.host_url, target)) 11 | return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc 12 | 13 | 14 | def redirect_back(default='blog.index', **kwargs): 15 | for target in request.args.get('next'), request.referrer: 16 | if not target: 17 | continue 18 | if is_safe_url(target): 19 | return redirect(target) 20 | return redirect(url_for(default, **kwargs)) 21 | 22 | 23 | def allowed_file(filename): 24 | return '.' in filename and Path(filename).suffix.lower() in current_app.config['GREYBOOK_ALLOWED_IMAGE_EXTENSIONS'] 25 | 26 | 27 | def random_filename(old_filename): 28 | ext = Path(old_filename).suffix 29 | new_filename = uuid.uuid4().hex + ext 30 | return new_filename 31 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/logs/.gitkeep -------------------------------------------------------------------------------- /migrations/README: -------------------------------------------------------------------------------- 1 | Single-database configuration for Flask. 2 | -------------------------------------------------------------------------------- /migrations/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # template used to generate migration files 5 | # file_template = %%(rev)s_%%(slug)s 6 | 7 | # set to 'true' to run the environment during 8 | # the 'revision' command, regardless of autogenerate 9 | # revision_environment = false 10 | 11 | 12 | # Logging configuration 13 | [loggers] 14 | keys = root,sqlalchemy,alembic,flask_migrate 15 | 16 | [handlers] 17 | keys = console 18 | 19 | [formatters] 20 | keys = generic 21 | 22 | [logger_root] 23 | level = WARN 24 | handlers = console 25 | qualname = 26 | 27 | [logger_sqlalchemy] 28 | level = WARN 29 | handlers = 30 | qualname = sqlalchemy.engine 31 | 32 | [logger_alembic] 33 | level = INFO 34 | handlers = 35 | qualname = alembic 36 | 37 | [logger_flask_migrate] 38 | level = INFO 39 | handlers = 40 | qualname = flask_migrate 41 | 42 | [handler_console] 43 | class = StreamHandler 44 | args = (sys.stderr,) 45 | level = NOTSET 46 | formatter = generic 47 | 48 | [formatter_generic] 49 | format = %(levelname)-5.5s [%(name)s] %(message)s 50 | datefmt = %H:%M:%S 51 | -------------------------------------------------------------------------------- /migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "greybook" 3 | version = "1.0.0" 4 | description = "A grey blog engine." 5 | authors = [ 6 | {name = "Grey Li", email = "withlihui@gmail.com"}, 7 | ] 8 | dependencies = [ 9 | "flask>=2.2.5", 10 | "flask-login>=0.6.2", 11 | "flask-wtf>=1.1.1", 12 | "flask-sqlalchemy>=3.0.3", 13 | "flask-migrate>=4.0.4", 14 | "bootstrap-flask>=2.2.0", 15 | "flask-ckeditor>=0.4.6", 16 | "email-validator>=2.0.0.post2", 17 | "flask-debugtoolbar>=0.13.1", 18 | "python-dotenv>=0.21.1", 19 | "flask-mailman>=0.3.0", 20 | "gunicorn>=22.0.0", 21 | ] 22 | requires-python = ">=3.9" 23 | license = {text = "MIT"} 24 | 25 | [tool.pdm] 26 | distribution = false 27 | 28 | [tool.pdm.dev-dependencies] 29 | dev = [ 30 | "watchdog>=3.0.0", 31 | "faker>=18.9.0", 32 | "ruff>=0.4.9", 33 | "pre-commit>=3.5.0", 34 | "pytest-cov>=5.0.0", 35 | "pytest>=8.2.2", 36 | ] 37 | 38 | [[tool.pdm.source]] 39 | url = "https://pypi.org/simple" 40 | verify_ssl = true 41 | name = "pypi" 42 | 43 | [[tool.pdm.autoexport]] 44 | filename = "requirements.txt" 45 | groups = ["default", "dev"] 46 | 47 | [tool.ruff] 48 | extend-exclude = ["migrations"] 49 | line-length = 120 50 | 51 | [tool.ruff.lint] 52 | select = [ 53 | # pycodestyle 54 | "E", 55 | # Pyflakes 56 | "F", 57 | # pyupgrade 58 | "UP", 59 | # flake8-bugbear 60 | "B", 61 | # flake8-simplify 62 | "SIM", 63 | # isort 64 | "I", 65 | ] 66 | 67 | [tool.ruff.format] 68 | quote-style = "single" 69 | 70 | [tool.ruff.lint.mccabe] 71 | max-complexity = 5 72 | 73 | [tool.pytest.ini_options] 74 | testpaths = ["tests"] 75 | addopts = ["--cov=greybook", "--cov-branch", "--cov-report=term-missing"] 76 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from greybook import create_app 4 | from greybook.core.extensions import db 5 | from greybook.models import Admin, Category, Comment, Link, Post 6 | 7 | 8 | class BaseTestCase(unittest.TestCase): 9 | def setUp(self): 10 | self.app = create_app('testing') 11 | self.context = self.app.app_context() 12 | self.context.push() 13 | self.client = self.app.test_client() 14 | self.cli_runner = self.app.test_cli_runner() 15 | 16 | db.create_all() 17 | user = Admin( 18 | name='Test Admin', 19 | username='admin', 20 | password='greybook', 21 | about='Test about page.', 22 | blog_title='Test Blog Title', 23 | blog_sub_title='Test sub title', 24 | ) 25 | category = Category(name='Test Category') 26 | post = Post(title='Test Post Title', category=category, body='Test post body') 27 | comment = Comment( 28 | author='Test comment author', email='test@example.com', body='Test comment body', post=post, reviewed=True 29 | ) 30 | link = Link(name='Test Link', url='http://example.com') 31 | db.session.add_all([user, category, post, comment, link]) 32 | db.session.commit() 33 | 34 | def tearDown(self): 35 | db.drop_all() 36 | self.context.pop() 37 | 38 | def login(self, username='admin', password='greybook'): 39 | return self.client.post('/auth/login', data=dict(username=username, password=password), follow_redirects=True) 40 | 41 | def logout(self): 42 | return self.client.get('/auth/logout', follow_redirects=True) 43 | -------------------------------------------------------------------------------- /tests/test_auth.py: -------------------------------------------------------------------------------- 1 | from tests import BaseTestCase 2 | 3 | 4 | class AuthTestCase(BaseTestCase): 5 | def test_login_user(self): 6 | response = self.login() 7 | data = response.get_data(as_text=True) 8 | self.assertIn('Welcome back.', data) 9 | 10 | def test_fail_login(self): 11 | response = self.login(username='wrong-username', password='wrong-password') 12 | data = response.get_data(as_text=True) 13 | self.assertIn('Invalid username or password.', data) 14 | 15 | def test_logout_user(self): 16 | self.login() 17 | response = self.logout() 18 | data = response.get_data(as_text=True) 19 | self.assertIn('Logout success.', data) 20 | 21 | def test_login_protect(self): 22 | response = self.client.get('/admin/settings', follow_redirects=True) 23 | data = response.get_data(as_text=True) 24 | self.assertIn('Please log in to access this page.', data) 25 | -------------------------------------------------------------------------------- /tests/test_basic.py: -------------------------------------------------------------------------------- 1 | from flask import current_app 2 | 3 | from tests import BaseTestCase 4 | 5 | 6 | class BasicTestCase(BaseTestCase): 7 | def test_app_exist(self): 8 | self.assertFalse(current_app is None) 9 | 10 | def test_app_is_testing(self): 11 | self.assertTrue(current_app.config['TESTING']) 12 | 13 | def test_404_error(self): 14 | response = self.client.get('/foo') 15 | data = response.get_data(as_text=True) 16 | self.assertEqual(response.status_code, 404) 17 | self.assertIn('404 Error', data) 18 | -------------------------------------------------------------------------------- /uploads/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greyli/greybook/e62bad5f14b7717f32811e7dcee9dc26dca05b60/uploads/.gitkeep --------------------------------------------------------------------------------